From df877a45b95c0aa41c9cd139b09d225ef5751979 Mon Sep 17 00:00:00 2001 From: hodu <92203597+Junyoung-WON@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:06:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=91=A5=EA=B7=BC=20=EB=AA=A8?= =?UTF-8?q?=EC=84=9C=EB=A6=AC=EC=9D=98=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20=EB=A1=9C=EB=93=9C=ED=95=98=EB=8A=94=20BindingAdapt?= =?UTF-8?q?ers=20=EC=B6=94=EA=B0=80=20#58=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 둥근 모서리로 이미지를 로딩하는 Glide 바인딩 어댑터 작성 - 세 속성이 모두 필요하다. - glideRoundedCornerImageUrl: 출력하고자 하는 이미지 url - glidePlaceHolder: placeHolder의 url - glideRoundingRadius: 모서리의 둥근 정도를 Int로 설정 * feat: 둥근 모서리로 이미지를 로딩하는 Coil 바인딩 어댑터 작성 - 세 속성이 모두 필요하다. - coilRoundedCornerImageUrl: 출력하고자 하는 이미지 url - coilPlaceHolder: placeHolder의 url - coilRoundingRadius: 모서리의 둥근 정도를 Float으로 설정 * fix: centerCrop 설정을 BindingAdapter 에 위임 - xml 속성으로 centerCrop을 주게 되면 Round Corner가 제대로 적용되지 않는 현상 발생 - Glide의 api로 제공되는 centerCrop() 메서드를 활용 --- .../staccato/presentation/BindingAdapters.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt index b745373df..a295c42cd 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/BindingAdapters.kt @@ -6,6 +6,8 @@ import androidx.databinding.BindingAdapter import coil.load import coil.transform.RoundedCornersTransformation import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions @BindingAdapter( value = ["coilImageUrl", "coilPlaceHolder"], @@ -34,6 +36,21 @@ fun ImageView.setCircleImageWithCoil( } } +@BindingAdapter( + value = ["coilRoundedCornerImageUrl", "coilPlaceHolder", "coilRoundingRadius"], +) +fun ImageView.setRoundedCornerImageWithCoil( + url: String?, + placeHolder: Drawable? = null, + roundingRadius: Float, +) { + load(url) { + placeholder(placeHolder) + transformations(RoundedCornersTransformation(roundingRadius)) + error(placeHolder) + } +} + @BindingAdapter( value = ["glideImageUrl", "glidePlaceHolder"], ) @@ -44,6 +61,7 @@ fun ImageView.loadImageWithGlide( Glide.with(context) .load(url) .placeholder(placeHolder) + .centerCrop() .error(placeHolder) .into(this) } @@ -62,3 +80,20 @@ fun ImageView.setCircleImageWithGlide( .error(placeHolder) .into(this) } + +@BindingAdapter( + value = ["glideRoundedCornerImageUrl", "glidePlaceHolder", "glideRoundingRadius"], +) +fun ImageView.setRoundedCornerImageWithGlide( + url: String?, + placeHolder: Drawable? = null, + roundingRadius: Int, +) { + Glide.with(context) + .load(url) + .placeholder(placeHolder) + .centerCrop() + .apply(RequestOptions.bitmapTransform(RoundedCorners(roundingRadius))) + .error(placeHolder) + .into(this) +} From 8bf48a036614504763a1da5c9b1199d44cdba741 Mon Sep 17 00:00:00 2001 From: hodu <92203597+Junyoung-WON@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:37:42 +0900 Subject: [PATCH 2/3] =?UTF-8?q?ui:=20=ED=83=80=EC=9E=84=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=20=ED=94=84=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8(BottomSheet)?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20#55=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ui: 타임라인 View xml 파일 작성 - 타임라인에 나타날 여행 상세 아이템 xml 작성 - 썸네일 사진 유무에 따라 뷰를 구분 - 타임라인이 나타날 fragment xml 작성 * feat: 타임라인 여행 상세 아이템 UI 모델 생성 * ui: 썸네일이 없는 여행상세 아이템의 margin 조정 * ui: Timeline RecyclerView의 layoutManager 설정 * ui: xml에서의 UiModel 데이터 바인딩 설정 * feat: ViewHolder 작성 - 썸네일 사진 유무에 따라 다른 ViewHolder로 구분 - 공통된 속성을 정의한 TimelineViewHolder 추상클래스 생성 * feat: TimelineRepository Interface 생성 * feat: 임시 TimelineRepository 구현체 생성 * feat: TimelineViewModel 및 Factory 생성 * feat: TimelineViewType 작성 * feat: TimelineAdapter 작성 * feat: TimelineFragment에 ViewModel과 Adapter 구현 * feat: 이미지 로딩 PlaceHolder drawable 추가 및 적용 * feat: 임시 데이터 연결 * ui: Timeline fragment 의 세부 설정 조정 * ui: Timeline의 Item xml 변경 - 뷰 타입을 3개로 분할: 첫 번째 아이템, 중간 아이템, 마지막 아이템 - 이에 따라 xml 파일 추가 및 view 수정 * feat: ViewType 변경에 따른 Adapter 및 ViewHolder 수정 * refactor: 불필요한 View 및 ViewHolder 제거 * feat: 여행 click 에 대한 event handler 생성 및 설정 * refactor: drawable 이름을 네이밍 컨벤션에 맞게 수정 * ui: RecyclerView의 마진 속성을 패딩 속성으로 변경 * feat: 바텀 시트 디자인 변경 및 툴바와의 상호작용 구현 * ui: 타임라인 글귀 추가 * style: ktlint check * ui: 둥근 모서리의 이미지로 변경 --- .../domain/repository/TimelineRepository.kt | 7 ++ .../presentation/main/MainActivity.kt | 50 +++++++++++++ .../timeline/TempTimelineRepository.kt | 71 +++++++++++++++++++ .../presentation/timeline/TimelineFragment.kt | 39 +++++++++- .../presentation/timeline/TimelineHandler.kt | 5 ++ .../timeline/TimelineTravelUiModel.kt | 8 +++ .../timeline/TimelineViewModel.kt | 16 +++++ .../timeline/TimelineViewModelFactory.kt | 16 +++++ .../timeline/adapter/FirstTravelViewHolder.kt | 15 ++++ .../timeline/adapter/LastTravelViewHolder.kt | 15 ++++ .../adapter/MiddleTravelViewHolder.kt | 15 ++++ .../timeline/adapter/TimelineAdapter.kt | 70 ++++++++++++++++++ .../timeline/adapter/TimelineViewHolder.kt | 14 ++++ .../timeline/adapter/TimelineViewType.kt | 25 +++++++ .../main/res/drawable/shape_all_gray2_4dp.xml | 5 ++ .../res/drawable/shape_timeline_dot_gray2.xml | 15 ++++ .../app/src/main/res/layout/activity_main.xml | 45 ++++++++++-- .../src/main/res/layout/fragment_timeline.xml | 35 +++++---- .../res/layout/item_fragment_timeline.xml | 64 +++++++++++++++++ .../layout_item_fragment_timeline_first.xml | 60 ++++++++++++++++ .../layout_item_fragment_timeline_last.xml | 60 ++++++++++++++++ .../layout_item_fragment_timeline_middle.xml | 58 +++++++++++++++ .../app/src/main/res/values/strings.xml | 1 + 23 files changed, 688 insertions(+), 21 deletions(-) create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/domain/repository/TimelineRepository.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TempTimelineRepository.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineHandler.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineTravelUiModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModel.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModelFactory.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/FirstTravelViewHolder.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/LastTravelViewHolder.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/MiddleTravelViewHolder.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineAdapter.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewHolder.kt create mode 100644 android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewType.kt create mode 100644 android/Staccato_AN/app/src/main/res/drawable/shape_all_gray2_4dp.xml create mode 100644 android/Staccato_AN/app/src/main/res/drawable/shape_timeline_dot_gray2.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/item_fragment_timeline.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/layout_item_fragment_timeline_first.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/layout_item_fragment_timeline_last.xml create mode 100644 android/Staccato_AN/app/src/main/res/layout/layout_item_fragment_timeline_middle.xml diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/domain/repository/TimelineRepository.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/domain/repository/TimelineRepository.kt new file mode 100644 index 000000000..ee8a34b7f --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/domain/repository/TimelineRepository.kt @@ -0,0 +1,7 @@ +package com.woowacourse.staccato.domain.repository + +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +interface TimelineRepository { + fun loadTravels(): List +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt index c4aff91ad..67627f95d 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/main/MainActivity.kt @@ -1,6 +1,7 @@ package com.woowacourse.staccato.presentation.main import android.os.Bundle +import android.view.View import android.widget.Toast import androidx.activity.addCallback import androidx.activity.result.contract.ActivityResultContracts @@ -69,6 +70,8 @@ class MainActivity : BindingActivity() { setupBottomSheetController() setupBottomSheetNavigation() setupBackPressedHandler() + setUpBottomSheetBehaviorAction() + setUpToolbar() } private fun setupBackPressedHandler() { @@ -134,4 +137,51 @@ class MainActivity : BindingActivity() { .setLaunchSingleTop(true) .setPopUpTo(popUpToId, false) .build() + + private fun setUpToolbar() { + binding.toolbarMain.setNavigationOnClickListener { + if (navController.currentDestination?.id == R.id.timelineFragment) { + behavior.state = STATE_COLLAPSED + } else { + navController.popBackStack() + } + } + } + + private fun setUpBottomSheetBehaviorAction() { + behavior.apply { + addBottomSheetCallback( + object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged( + bottomSheet: View, + newState: Int, + ) { + when (newState) { + STATE_COLLAPSED -> { + binding.toolbarMain.visibility = View.INVISIBLE + } + + STATE_EXPANDED -> { + binding.btnTimeline.visibility = View.INVISIBLE + } + + else -> { + binding.toolbarMain.visibility = View.VISIBLE + binding.btnTimeline.visibility = View.VISIBLE + } + } + } + + override fun onSlide( + bottomSheet: View, + slideOffset: Float, + ) { + binding.tvBottomSheetRemindYourMemories.alpha = 1 - slideOffset + binding.btnTimeline.alpha = 1 - slideOffset + binding.toolbarMain.alpha = slideOffset + } + }, + ) + } + } } diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TempTimelineRepository.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TempTimelineRepository.kt new file mode 100644 index 000000000..9db0788a1 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TempTimelineRepository.kt @@ -0,0 +1,71 @@ +package com.woowacourse.staccato.presentation.timeline + +import com.woowacourse.staccato.domain.repository.TimelineRepository + +class TempTimelineRepository : TimelineRepository { + private val travels = + listOf( + TimelineTravelUiModel( + travelId = 0L, + travelThumbnail = null, + travelPeriod = "2024.07.23", + travelTitle = "우테코 선릉캠 탐방", + ), + TimelineTravelUiModel( + travelId = 1L, + travelThumbnail = "https://cdn.tourtoctoc.com/news/photo/202305/520_2742_5617.jpg", + travelPeriod = "2024.06.30 - 07.04", + travelTitle = "제주도 여행", + ), + TimelineTravelUiModel( + travelId = 2L, + travelThumbnail = null, + travelPeriod = "2024.06.28", + travelTitle = "파리 여행", + ), + TimelineTravelUiModel( + travelId = 3L, + travelThumbnail = + "https://pds.joongang.co.kr/news/component/htmlphoto_mmdata/" + + "202203/11/97c3e727-0d4c-4fba-83c7-7558d9455651.jpg", + travelPeriod = "2024.06.26", + travelTitle = "포항 영일대 당일치기", + ), + TimelineTravelUiModel( + travelId = 4L, + travelThumbnail = + "https://triptogo.world/web/product/big/" + + "202104/e827b41e2d22aeddc8015b018df9aa5b.png", + travelPeriod = "2024.05.28 - 29", + travelTitle = "서울 나들이", + ), + TimelineTravelUiModel( + travelId = 5L, + travelThumbnail = + "https://triptogo.world/web/product/big/" + + "202104/e827b41e2d22aeddc8015b018df9aa5b.png", + travelPeriod = "2024.05.28 - 29", + travelTitle = "서울 나들이", + ), + TimelineTravelUiModel( + travelId = 6L, + travelThumbnail = + "https://triptogo.world/web/product/big/" + + "202104/e827b41e2d22aeddc8015b018df9aa5b.png", + travelPeriod = "2024.05.28 - 29", + travelTitle = "서울 나들이", + ), + TimelineTravelUiModel( + travelId = 7L, + travelThumbnail = + "https://triptogo.world/web/product/big/" + + "202104/e827b41e2d22aeddc8015b018df9aa5b.png", + travelPeriod = "2024.05.28 - 29", + travelTitle = "서울 나들이", + ), + ) + + override fun loadTravels(): List { + return travels + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt index b327cbcd7..324439d16 100644 --- a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineFragment.kt @@ -2,18 +2,51 @@ package com.woowacourse.staccato.presentation.timeline import android.os.Bundle import android.view.View +import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import com.woowacourse.staccato.R import com.woowacourse.staccato.databinding.FragmentTimelineBinding import com.woowacourse.staccato.presentation.base.BindingFragment +import com.woowacourse.staccato.presentation.timeline.adapter.TimelineAdapter + +class TimelineFragment : + BindingFragment(R.layout.fragment_timeline), + TimelineHandler { + private lateinit var viewModel: TimelineViewModel + private lateinit var adapter: TimelineAdapter -class TimelineFragment : BindingFragment(R.layout.fragment_timeline) { override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { - binding.btnTimeline.setOnClickListener { - findNavController().navigate(R.id.action_timelineFragment_to_travelFragment) + setUpViewModel() + setUpAdapter() + setUpObserving() + viewModel.loadTimeline() + } + + private fun setUpViewModel() { + val viewModelFactory = TimelineViewModelFactory(TempTimelineRepository()) + viewModel = ViewModelProvider(this, viewModelFactory)[TimelineViewModel::class.java] + } + + private fun setUpAdapter() { + adapter = TimelineAdapter(this) + binding.rvTimeline.adapter = adapter + } + + private fun setUpObserving() { + viewModel.travels.observe(viewLifecycleOwner) { timeline -> + adapter.setTravels(timeline) } } + + private fun navigateToTravel() { + findNavController().navigate(R.id.action_timelineFragment_to_travelFragment) + } + + override fun onTravelClicked(travelId: Long) { + // Log.d("ㅌㅅㅌ", "clicked item: $travelId") + navigateToTravel() + } } diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineHandler.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineHandler.kt new file mode 100644 index 000000000..4fc27f70c --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineHandler.kt @@ -0,0 +1,5 @@ +package com.woowacourse.staccato.presentation.timeline + +interface TimelineHandler { + fun onTravelClicked(travelId: Long) +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineTravelUiModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineTravelUiModel.kt new file mode 100644 index 000000000..cdae93837 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineTravelUiModel.kt @@ -0,0 +1,8 @@ +package com.woowacourse.staccato.presentation.timeline + +data class TimelineTravelUiModel( + val travelId: Long, + val travelThumbnail: String? = null, + val travelPeriod: String, + val travelTitle: String, +) diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModel.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModel.kt new file mode 100644 index 000000000..c6d9d8913 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModel.kt @@ -0,0 +1,16 @@ +package com.woowacourse.staccato.presentation.timeline + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.woowacourse.staccato.domain.repository.TimelineRepository + +class TimelineViewModel(private val repository: TimelineRepository) : ViewModel() { + private val _travels = MutableLiveData>() + val travels: LiveData> + get() = _travels + + fun loadTimeline() { + _travels.value = repository.loadTravels() + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModelFactory.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModelFactory.kt new file mode 100644 index 000000000..6e86959f7 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/TimelineViewModelFactory.kt @@ -0,0 +1,16 @@ +package com.woowacourse.staccato.presentation.timeline + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.woowacourse.staccato.domain.repository.TimelineRepository + +class TimelineViewModelFactory(private val repository: TimelineRepository) : + ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(TimelineViewModel::class.java)) { + return TimelineViewModel(repository) as T + } else { + throw IllegalArgumentException("확인되지 않은 ViewModel 클래스입니다.") + } + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/FirstTravelViewHolder.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/FirstTravelViewHolder.kt new file mode 100644 index 000000000..161a52adf --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/FirstTravelViewHolder.kt @@ -0,0 +1,15 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineFirstBinding +import com.woowacourse.staccato.presentation.timeline.TimelineHandler +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +class FirstTravelViewHolder( + private val binding: LayoutItemFragmentTimelineFirstBinding, + private val eventHandler: TimelineHandler, +) : TimelineViewHolder(binding, eventHandler) { + override fun bind(item: TimelineTravelUiModel) { + binding.travel = item + binding.eventHandler = eventHandler + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/LastTravelViewHolder.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/LastTravelViewHolder.kt new file mode 100644 index 000000000..450b15f6d --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/LastTravelViewHolder.kt @@ -0,0 +1,15 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineLastBinding +import com.woowacourse.staccato.presentation.timeline.TimelineHandler +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +class LastTravelViewHolder( + private val binding: LayoutItemFragmentTimelineLastBinding, + private val eventHandler: TimelineHandler, +) : TimelineViewHolder(binding, eventHandler) { + override fun bind(item: TimelineTravelUiModel) { + binding.travel = item + binding.eventHandler = eventHandler + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/MiddleTravelViewHolder.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/MiddleTravelViewHolder.kt new file mode 100644 index 000000000..285ac619f --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/MiddleTravelViewHolder.kt @@ -0,0 +1,15 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineMiddleBinding +import com.woowacourse.staccato.presentation.timeline.TimelineHandler +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +class MiddleTravelViewHolder( + private val binding: LayoutItemFragmentTimelineMiddleBinding, + private val eventHandler: TimelineHandler, +) : TimelineViewHolder(binding, eventHandler) { + override fun bind(item: TimelineTravelUiModel) { + binding.travel = item + binding.eventHandler = eventHandler + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineAdapter.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineAdapter.kt new file mode 100644 index 000000000..2221c010e --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineAdapter.kt @@ -0,0 +1,70 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineFirstBinding +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineLastBinding +import com.woowacourse.staccato.databinding.LayoutItemFragmentTimelineMiddleBinding +import com.woowacourse.staccato.presentation.timeline.TimelineHandler +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +class TimelineAdapter(private val eventHandler: TimelineHandler) : + RecyclerView.Adapter() { + private var travels = emptyList() + + override fun getItemViewType(position: Int): Int { + return TimelineViewType.fromPosition(position, itemCount).ordinal + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): TimelineViewHolder { + return when (viewType) { + TimelineViewType.FIRST_ITEM.ordinal -> { + val binding = + LayoutItemFragmentTimelineFirstBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + FirstTravelViewHolder(binding, eventHandler) + } + + TimelineViewType.MIDDLE_ITEM.ordinal -> { + val binding = + LayoutItemFragmentTimelineMiddleBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + MiddleTravelViewHolder(binding, eventHandler) + } + + else -> { + val binding = + LayoutItemFragmentTimelineLastBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + LastTravelViewHolder(binding, eventHandler) + } + } + } + + override fun getItemCount(): Int = travels.size + + override fun onBindViewHolder( + holder: TimelineViewHolder, + position: Int, + ) { + holder.bind(travels[position]) + } + + fun setTravels(newTravels: List) { + travels = newTravels + notifyItemRangeInserted(0, newTravels.size) + } +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewHolder.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewHolder.kt new file mode 100644 index 000000000..675457591 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewHolder.kt @@ -0,0 +1,14 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.RecyclerView +import com.woowacourse.staccato.presentation.timeline.TimelineHandler +import com.woowacourse.staccato.presentation.timeline.TimelineTravelUiModel + +abstract class TimelineViewHolder( + binding: ViewDataBinding, + private val eventHandler: TimelineHandler, +) : + RecyclerView.ViewHolder(binding.root) { + abstract fun bind(item: TimelineTravelUiModel) +} diff --git a/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewType.kt b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewType.kt new file mode 100644 index 000000000..ac266a2d9 --- /dev/null +++ b/android/Staccato_AN/app/src/main/java/com/woowacourse/staccato/presentation/timeline/adapter/TimelineViewType.kt @@ -0,0 +1,25 @@ +package com.woowacourse.staccato.presentation.timeline.adapter + +enum class TimelineViewType { + FIRST_ITEM, + MIDDLE_ITEM, + LAST_ITEM, ; + + companion object { + private const val FIRST_INDEX = 0 + private const val LAST_INDEX_ADJUSTMENT = 1 + + fun fromPosition( + position: Int, + totalSize: Int, + ): TimelineViewType { + return if (position == FIRST_INDEX) { + FIRST_ITEM + } else if (position < totalSize - LAST_INDEX_ADJUSTMENT) { + MIDDLE_ITEM + } else { + LAST_ITEM + } + } + } +} diff --git a/android/Staccato_AN/app/src/main/res/drawable/shape_all_gray2_4dp.xml b/android/Staccato_AN/app/src/main/res/drawable/shape_all_gray2_4dp.xml new file mode 100644 index 000000000..f2d24ad5f --- /dev/null +++ b/android/Staccato_AN/app/src/main/res/drawable/shape_all_gray2_4dp.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/Staccato_AN/app/src/main/res/drawable/shape_timeline_dot_gray2.xml b/android/Staccato_AN/app/src/main/res/drawable/shape_timeline_dot_gray2.xml new file mode 100644 index 000000000..d5be98962 --- /dev/null +++ b/android/Staccato_AN/app/src/main/res/drawable/shape_timeline_dot_gray2.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/android/Staccato_AN/app/src/main/res/layout/activity_main.xml b/android/Staccato_AN/app/src/main/res/layout/activity_main.xml index 332410294..11f5faa49 100644 --- a/android/Staccato_AN/app/src/main/res/layout/activity_main.xml +++ b/android/Staccato_AN/app/src/main/res/layout/activity_main.xml @@ -53,20 +53,39 @@ + + + + + + + + + + + + diff --git a/android/Staccato_AN/app/src/main/res/layout/fragment_timeline.xml b/android/Staccato_AN/app/src/main/res/layout/fragment_timeline.xml index 7842d1605..e401d438f 100644 --- a/android/Staccato_AN/app/src/main/res/layout/fragment_timeline.xml +++ b/android/Staccato_AN/app/src/main/res/layout/fragment_timeline.xml @@ -10,28 +10,35 @@ -