fix(home): 라이브 전체 버튼을 오른쪽에 고정한다

This commit is contained in:
2026-08-04 11:45:29 +09:00
parent d20d6aa971
commit 3c21a764ae
8 changed files with 236 additions and 98 deletions

View File

@@ -215,7 +215,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
setOnBannerClick { onBannerClick(it) }
}
liveAdapter.setOnLiveClick { onLiveClick(it) }
liveAdapter.setOnMoreClick { openHomeOnAirLive() }
binding.tvHomeLiveAll.setOnClickListener { openHomeOnAirLive() }
binding.rvHomeLives.adapter = liveAdapter
binding.rvHomeRecentActivityCreators.adapter = recentActivityCreatorAdapter
binding.rvHomeRecentDebutCreators.adapter = recentDebutCreatorAdapter

View File

@@ -1,10 +1,7 @@
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
@@ -12,15 +9,12 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationLiveUiModel
import kr.co.vividnext.sodalive.v2.widget.livethumbnail.LiveThumbnailItem
import kr.co.vividnext.sodalive.v2.widget.livethumbnail.LiveThumbnailSimpleView
class HomeLiveAdapter : RecyclerView.Adapter<HomeLiveAdapter.LiveViewHolder>() {
class HomeLiveAdapter : RecyclerView.Adapter<HomeLiveAdapter.LiveItemViewHolder>() {
private var items: List<HomeRecommendationLiveUiModel> = emptyList()
private var showAllItem: Boolean = false
private var onClick: ((HomeRecommendationLiveUiModel) -> Unit)? = null
private var onMoreClick: (() -> Unit)? = null
fun submitItems(items: List<HomeRecommendationLiveUiModel>) {
this.items = items.take(MAX_VISIBLE_LIVE_COUNT)
showAllItem = items.isNotEmpty()
notifyDataSetChanged()
}
@@ -28,32 +22,15 @@ class HomeLiveAdapter : RecyclerView.Adapter<HomeLiveAdapter.LiveViewHolder>() {
onClick = listener
}
fun setOnMoreClick(listener: (() -> Unit)?) {
onMoreClick = listener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LiveItemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.view_live_thumbnail_simple, parent, false)
view.layoutParams = liveItemLayoutParams(parent)
return LiveItemViewHolder(view as LiveThumbnailSimpleView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LiveViewHolder {
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: LiveItemViewHolder, position: Int) = holder.bind(items[position], onClick)
override fun onBindViewHolder(holder: LiveViewHolder, position: Int) {
when (holder) {
is LiveItemViewHolder -> holder.bind(items[position], onClick)
is MoreViewHolder -> holder.bind(onMoreClick)
}
}
override fun getItemCount(): Int = items.size + if (showAllItem) 1 else 0
override fun getItemViewType(position: Int): Int {
return if (showAllItem && position == itemCount - 1) VIEW_TYPE_MORE else VIEW_TYPE_LIVE
}
override fun getItemCount(): Int = items.size
private fun liveItemLayoutParams(parent: ViewGroup): RecyclerView.LayoutParams {
return RecyclerView.LayoutParams(
@@ -62,25 +39,9 @@ class HomeLiveAdapter : RecyclerView.Adapter<HomeLiveAdapter.LiveViewHolder>() {
).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
) : LiveViewHolder(view) {
) : RecyclerView.ViewHolder(view) {
fun bind(
item: HomeRecommendationLiveUiModel,
onClick: ((HomeRecommendationLiveUiModel) -> Unit)?
@@ -99,17 +60,7 @@ class HomeLiveAdapter : RecyclerView.Adapter<HomeLiveAdapter.LiveViewHolder>() {
}
}
class MoreViewHolder(
private val view: TextView
) : LiveViewHolder(view) {
fun bind(onClick: (() -> Unit)?) {
view.setOnClickListener(if (onClick == null) null else View.OnClickListener { onClick.invoke() })
}
}
companion object {
private const val MAX_VISIBLE_LIVE_COUNT = 20
private const val VIEW_TYPE_LIVE = 0
private const val VIEW_TYPE_MORE = 1
}
}

View File

@@ -51,17 +51,30 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_12"
android:orientation="vertical">
android:orientation="horizontal"
android:visibility="gone">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_home_lives"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/spacing_20"
android:paddingStart="@dimen/spacing_20"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/view_live_thumbnail_simple" />
<TextView
android:id="@+id/tv_home_live_all"
style="@style/Typography.Body5"
android:layout_width="@dimen/home_live_more_width"
android:layout_height="@dimen/home_live_row_height"
android:layout_marginEnd="@dimen/spacing_20"
android:background="@color/black"
android:gravity="center"
android:text="@string/screen_home_theme_all"
android:textColor="@color/soda_400" />
</LinearLayout>
<LinearLayout

View File

@@ -1044,54 +1044,29 @@ class HomeMainFragmentLayoutTest {
}
@Test
fun `홈 라이브 어댑터는 라이브가 있으면 전체 항목을 추가한다`() {
val context = ApplicationProvider.getApplicationContext<Context>()
val parent = RecyclerView(context)
parent.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
fun `홈 라이브 어댑터는 최대 스무 개 라이브만 표시한다`() {
val adapter = HomeLiveAdapter()
adapter.submitItems(emptyList())
assertEquals(0, adapter.itemCount)
adapter.submitItems(listOf(liveItem(1L)))
assertEquals(2, adapter.itemCount)
val viewHolder = adapter.onCreateViewHolder(parent, adapter.getItemViewType(1))
assertEquals(
context.getString(R.string.screen_home_theme_all),
(viewHolder.itemView as TextView).text.toString()
)
assertEquals(1, adapter.itemCount)
adapter.submitItems((1L..20L).map(::liveItem))
assertEquals(21, adapter.itemCount)
}
@Test
fun `home live adapter appends all item after twenty lives`() {
val context = ApplicationProvider.getApplicationContext<Context>()
val parent = RecyclerView(context)
parent.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
val adapter = HomeLiveAdapter()
assertEquals(20, adapter.itemCount)
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)
assertEquals(20, adapter.itemCount)
}
@Test
fun `home live adapter caps lives before all item`() {
fun `홈 라이브 어댑터는 스물두 개 입력도 스무 개로 제한한다`() {
val adapter = HomeLiveAdapter()
adapter.submitItems((1L..22L).map(::liveItem))
assertEquals(21, adapter.itemCount)
assertEquals(20, adapter.itemCount)
}
@Test

View File

@@ -210,6 +210,17 @@ class HomeMainFragmentLoginGuardSourceTest {
)
}
@Test
fun `HomeMainFragment 라이브 전체 버튼은 Fragment에서 공통 라이브 화면으로 이동한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt"
).readText()
val setupSource = source.substringFrom("private fun setUpRecommendationAdapters()")
assertTrue(setupSource.contains("binding.tvHomeLiveAll.setOnClickListener { openHomeOnAirLive() }"))
assertFalse(setupSource.contains("liveAdapter.setOnMoreClick"))
}
@Test
fun `HomeMainFragment loading dialog는 통합 loading state로 제어한다`() {
val source = projectFile(