diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt index 8f87e7cb..7345f6c9 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt @@ -1,7 +1,10 @@ package kr.co.vividnext.sodalive.v2.main.home.ui +import android.view.Gravity import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.extensions.loadUrl @@ -11,10 +14,12 @@ import kr.co.vividnext.sodalive.v2.widget.livethumbnail.LiveThumbnailSimpleView class HomeLiveAdapter : RecyclerView.Adapter() { private var items: List = emptyList() + private var hasMore: Boolean = false private var onClick: ((HomeRecommendationLiveUiModel) -> Unit)? = null fun submitItems(items: List) { - this.items = items + this.items = items.take(MAX_VISIBLE_LIVE_COUNT) + hasMore = items.size > MAX_VISIBLE_LIVE_COUNT notifyDataSetChanged() } @@ -23,20 +28,54 @@ class HomeLiveAdapter : RecyclerView.Adapter() { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LiveViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.view_live_thumbnail_simple, parent, false) - view.layoutParams = recyclerItemLayoutParams(parent) - return LiveViewHolder(view as LiveThumbnailSimpleView) + return if (viewType == VIEW_TYPE_MORE) { + MoreViewHolder(createMoreView(parent)) + } else { + val view = LayoutInflater.from(parent.context).inflate(R.layout.view_live_thumbnail_simple, parent, false) + view.layoutParams = liveItemLayoutParams(parent) + LiveItemViewHolder(view as LiveThumbnailSimpleView) + } } override fun onBindViewHolder(holder: LiveViewHolder, position: Int) { - holder.bind(items[position], onClick) + when (holder) { + is LiveItemViewHolder -> holder.bind(items[position], onClick) + is MoreViewHolder -> holder.bind() + } } - override fun getItemCount(): Int = items.size + override fun getItemCount(): Int = items.size + if (hasMore) 1 else 0 - class LiveViewHolder( + override fun getItemViewType(position: Int): Int { + return if (hasMore && position == itemCount - 1) VIEW_TYPE_MORE else VIEW_TYPE_LIVE + } + + private fun liveItemLayoutParams(parent: ViewGroup): RecyclerView.LayoutParams { + return RecyclerView.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { marginEnd = parent.resources.getDimensionPixelSize(R.dimen.spacing_14) } + } + + private fun createMoreView(parent: ViewGroup): TextView { + return TextView(parent.context).apply { + layoutParams = RecyclerView.LayoutParams( + parent.resources.getDimensionPixelSize(R.dimen.home_live_more_width), + parent.resources.getDimensionPixelSize(R.dimen.home_live_row_height) + ) + gravity = Gravity.CENTER + setBackgroundResource(R.color.black) + setText(R.string.screen_home_theme_all) + setTextAppearance(R.style.Typography_Body5) + setTextColor(parent.context.getColor(R.color.soda_400)) + } + } + + sealed class LiveViewHolder(view: View) : RecyclerView.ViewHolder(view) + + class LiveItemViewHolder( private val view: LiveThumbnailSimpleView - ) : RecyclerView.ViewHolder(view) { + ) : LiveViewHolder(view) { fun bind( item: HomeRecommendationLiveUiModel, onClick: ((HomeRecommendationLiveUiModel) -> Unit)? @@ -54,4 +93,18 @@ class HomeLiveAdapter : RecyclerView.Adapter() { view.setOnLiveThumbnailClick(if (onClick == null) null else { _: LiveThumbnailItem -> onClick.invoke(item) }) } } + + class MoreViewHolder( + private val view: TextView + ) : LiveViewHolder(view) { + fun bind() { + view.setOnClickListener(null) + } + } + + companion object { + private const val MAX_VISIBLE_LIVE_COUNT = 20 + private const val VIEW_TYPE_LIVE = 0 + private const val VIEW_TYPE_MORE = 1 + } } diff --git a/app/src/main/res/layout/fragment_v2_main_home.xml b/app/src/main/res/layout/fragment_v2_main_home.xml index 67d89831..21ee939a 100644 --- a/app/src/main/res/layout/fragment_v2_main_home.xml +++ b/app/src/main/res/layout/fragment_v2_main_home.xml @@ -39,19 +39,20 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingBottom="@dimen/spacing_32"> + android:paddingBottom="@dimen/spacing_28"> + android:layout_marginTop="@dimen/spacing_12" + android:orientation="vertical"> + android:paddingTop="@dimen/spacing_28"> 32dp 48dp + 58dp + 102dp + 4dp 8dp 14dp diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt index 0c2c1fa0..5d13822b 100644 --- a/app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt @@ -2,15 +2,20 @@ package kr.co.vividnext.sodalive.v2.main.home import android.app.Application import android.content.Context +import android.graphics.drawable.ColorDrawable import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.core.widget.NestedScrollView +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ApplicationProvider import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationLiveUiModel +import kr.co.vividnext.sodalive.v2.main.home.ui.HomeLiveAdapter import kr.co.vividnext.sodalive.v2.widget.TextTabBarView import kr.co.vividnext.sodalive.v2.widget.banner.BannerView import org.junit.Assert.assertEquals @@ -119,6 +124,56 @@ class HomeMainFragmentLayoutTest { assertEquals(View.GONE, popularCommunitySection.visibility) } + @Test + fun `home live section matches figma row dimensions`() { + val root = inflateView(R.layout.fragment_v2_main_home) + val liveList = root.findViewById(R.id.rv_home_lives) + + assertNotNull(liveList) + assertEquals(102.dpToPx(), liveList.layoutParams.height) + assertEquals(20.dpToPx(), liveList.paddingStart) + } + + @Test + fun `home live adapter uses figma item gap`() { + val context = ApplicationProvider.getApplicationContext() + val parent = RecyclerView(context) + parent.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + val viewHolder = HomeLiveAdapter().onCreateViewHolder(parent, 0) + val layoutParams = viewHolder.itemView.layoutParams as ViewGroup.MarginLayoutParams + + assertEquals(14.dpToPx(), layoutParams.marginEnd) + } + + @Test + fun `home live adapter appends all item after twenty lives`() { + val context = ApplicationProvider.getApplicationContext() + val parent = RecyclerView(context) + parent.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + val adapter = HomeLiveAdapter() + + adapter.submitItems((1L..21L).map(::liveItem)) + val viewHolder = adapter.onCreateViewHolder(parent, adapter.getItemViewType(20)) + adapter.onBindViewHolder(viewHolder, 20) + val moreText = viewHolder.itemView as TextView + + assertEquals(21, adapter.itemCount) + assertEquals(context.getString(R.string.screen_home_theme_all), moreText.text.toString()) + assertEquals(58.dpToPx(), moreText.layoutParams.width) + assertEquals(102.dpToPx(), moreText.layoutParams.height) + assertEquals(context.getColor(R.color.soda_400), moreText.currentTextColor) + assertEquals(context.getColor(R.color.black), (moreText.background as ColorDrawable).color) + } + + @Test + fun `home live adapter caps lives before all item`() { + val adapter = HomeLiveAdapter() + + adapter.submitItems((1L..22L).map(::liveItem)) + + assertEquals(21, adapter.itemCount) + } + @Test fun `home layout uses section title components and custom genre title row`() { val root = inflateView(R.layout.fragment_v2_main_home) @@ -193,4 +248,15 @@ class HomeMainFragmentLayoutTest { val context = ApplicationProvider.getApplicationContext() return (this * context.resources.displayMetrics.density).toInt() } + + private fun liveItem(id: Long): HomeRecommendationLiveUiModel { + return HomeRecommendationLiveUiModel( + liveId = id, + creatorId = id, + imageUrl = null, + title = "title$id", + creatorNickname = "creator$id", + beginDateTime = null + ) + } }