fix(content): 전체 탭 grid 폭을 주입한다
This commit is contained in:
@@ -8,6 +8,7 @@ import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -45,6 +46,7 @@ import kr.co.vividnext.sodalive.v2.main.content.model.toContentBannerIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.toContentBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.usesDayOfWeekQuery
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.usesSeriesItems
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.CONTENT_ALL_GRID_SPAN_COUNT
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.CONTENT_RECOMMENDED_GRID_SPAN_COUNT
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.ContentAllAudioCardAdapter
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.ContentAllSeriesCardAdapter
|
||||
@@ -55,6 +57,7 @@ import kr.co.vividnext.sodalive.v2.main.content.ui.ContentNewAndHotAdapter
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.ContentOriginalSeriesAdapter
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.addContentGridItemSpacing
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.addContentHorizontalItemSpacing
|
||||
import kr.co.vividnext.sodalive.v2.main.content.ui.calculateContentGridItemWidthPx
|
||||
import kr.co.vividnext.sodalive.v2.widget.AudioContentCardSize
|
||||
import kr.co.vividnext.sodalive.v2.widget.contentranking.ContentRankingAdapter
|
||||
import kr.co.vividnext.sodalive.v2.widget.contentranking.ContentRankingItem
|
||||
@@ -281,6 +284,7 @@ class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
|
||||
layoutManager = contentAllGridLayoutManager
|
||||
adapter = contentAllAudioCardAdapter
|
||||
addContentGridItemSpacing(CONTENT_ALL_GRID_SPAN_COUNT)
|
||||
doOnLayout { updateAllTabGridItemWidth() }
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
@@ -357,6 +361,7 @@ class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
|
||||
bindAllTabControls(state)
|
||||
binding.layoutContentAllSurface.visibility = View.VISIBLE
|
||||
hideAllTabEmptyError()
|
||||
updateAllTabGridItemWidth()
|
||||
if (state.selectedType.usesSeriesItems()) {
|
||||
binding.rvContentAllItems.adapter = contentAllSeriesCardAdapter
|
||||
contentAllAudioCardAdapter.submitItems(emptyList())
|
||||
@@ -372,6 +377,12 @@ class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAllTabGridItemWidth() {
|
||||
val widthPx = binding.rvContentAllItems.calculateContentGridItemWidthPx(CONTENT_ALL_GRID_SPAN_COUNT)
|
||||
contentAllAudioCardAdapter.setGridItemWidthPx(widthPx)
|
||||
contentAllSeriesCardAdapter.setGridItemWidthPx(widthPx)
|
||||
}
|
||||
|
||||
private fun bindAllTabEmpty(state: MainContentAllTabUiState.Empty) {
|
||||
bindAllTabControls(state)
|
||||
binding.layoutContentAllSurface.visibility = View.VISIBLE
|
||||
@@ -589,6 +600,5 @@ class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
|
||||
private const val CONTENT_TAB_RECOMMENDATION = 0
|
||||
private const val CONTENT_TAB_RANKING = 1
|
||||
private const val CONTENT_TAB_ALL = 2
|
||||
private const val CONTENT_ALL_GRID_SPAN_COUNT = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,19 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.databinding.ItemContentAudioCardBinding
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.MainContentAllAudioUiModel
|
||||
import kr.co.vividnext.sodalive.v2.widget.AudioContentCardSize
|
||||
|
||||
class ContentAllAudioCardAdapter(
|
||||
private val onAudioClick: (Long) -> Unit = {}
|
||||
) : RecyclerView.Adapter<ContentAllAudioCardAdapter.ViewHolder>() {
|
||||
|
||||
private var items: List<MainContentAllAudioUiModel> = emptyList()
|
||||
private var gridItemWidthPx: Int = 0
|
||||
|
||||
fun setGridItemWidthPx(widthPx: Int) {
|
||||
if (widthPx <= 0 || gridItemWidthPx == widthPx) return
|
||||
gridItemWidthPx = widthPx
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun submitItems(items: List<MainContentAllAudioUiModel>) {
|
||||
this.items = items
|
||||
@@ -27,7 +33,10 @@ class ContentAllAudioCardAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
holder.bind(
|
||||
item = items[position],
|
||||
gridItemWidthPx = gridItemWidthPx
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
@@ -36,8 +45,8 @@ class ContentAllAudioCardAdapter(
|
||||
private val binding: ItemContentAudioCardBinding,
|
||||
private val onAudioClick: (Long) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: MainContentAllAudioUiModel) = with(binding.audioContentCard.root) {
|
||||
setSize(AudioContentCardSize.Small)
|
||||
fun bind(item: MainContentAllAudioUiModel, gridItemWidthPx: Int) = with(binding.audioContentCard.root) {
|
||||
setGridItemWidthPx(gridItemWidthPx)
|
||||
setContent(item.title, item.creatorNickname)
|
||||
setTags(item.tags)
|
||||
setAdultVisible(item.showAdultBadge)
|
||||
|
||||
@@ -6,13 +6,19 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.databinding.ItemContentAllSeriesCardBinding
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.v2.main.content.model.MainContentAllSeriesUiModel
|
||||
import kr.co.vividnext.sodalive.v2.widget.SeriesContentCardSize
|
||||
|
||||
class ContentAllSeriesCardAdapter(
|
||||
private val onSeriesClick: (Long) -> Unit = {}
|
||||
) : RecyclerView.Adapter<ContentAllSeriesCardAdapter.ViewHolder>() {
|
||||
|
||||
private var items: List<MainContentAllSeriesUiModel> = emptyList()
|
||||
private var gridItemWidthPx: Int = 0
|
||||
|
||||
fun setGridItemWidthPx(widthPx: Int) {
|
||||
if (widthPx <= 0 || gridItemWidthPx == widthPx) return
|
||||
gridItemWidthPx = widthPx
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun submitItems(items: List<MainContentAllSeriesUiModel>) {
|
||||
this.items = items
|
||||
@@ -27,7 +33,10 @@ class ContentAllSeriesCardAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
holder.bind(
|
||||
item = items[position],
|
||||
gridItemWidthPx = gridItemWidthPx
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
@@ -36,8 +45,8 @@ class ContentAllSeriesCardAdapter(
|
||||
private val binding: ItemContentAllSeriesCardBinding,
|
||||
private val onSeriesClick: (Long) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: MainContentAllSeriesUiModel) = with(binding.seriesContentCard.root) {
|
||||
setSize(SeriesContentCardSize.Small)
|
||||
fun bind(item: MainContentAllSeriesUiModel, gridItemWidthPx: Int) = with(binding.seriesContentCard.root) {
|
||||
setGridItemWidthPx(gridItemWidthPx)
|
||||
setContent(item.title, item.creatorNickname)
|
||||
setOriginalVisible(item.showOriginalTag)
|
||||
setAdultVisible(item.showAdultBadge)
|
||||
|
||||
@@ -14,6 +14,13 @@ fun RecyclerView.addContentGridItemSpacing(spanCount: Int = CONTENT_RECOMMENDED_
|
||||
if (itemDecorationCount == 0) addItemDecoration(ContentGridItemDecoration(spanCount))
|
||||
}
|
||||
|
||||
fun RecyclerView.calculateContentGridItemWidthPx(spanCount: Int): Int {
|
||||
val availableWidth = measuredWidth - paddingLeft - paddingRight
|
||||
if (availableWidth <= 0 || spanCount <= 0) return 0
|
||||
val totalGap = GRID_ITEM_GAP_DP.dpToPx() * (spanCount - 1)
|
||||
return ((availableWidth - totalGap) / spanCount).roundToInt()
|
||||
}
|
||||
|
||||
private class ContentHorizontalItemDecoration : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
@@ -41,3 +48,4 @@ private const val HORIZONTAL_ITEM_GAP_DP = 8
|
||||
private const val GRID_ITEM_GAP_DP = 8
|
||||
private const val GRID_ITEM_VERTICAL_GAP_DP = 28
|
||||
const val CONTENT_RECOMMENDED_GRID_SPAN_COUNT = 2
|
||||
const val CONTENT_ALL_GRID_SPAN_COUNT = 3
|
||||
|
||||
@@ -119,6 +119,10 @@ class ContentMainFragmentSourceTest {
|
||||
assertTrue(source.contains("CONTENT_ALL_GRID_SPAN_COUNT"))
|
||||
assertTrue(source.contains("GridLayoutManager(requireContext(), CONTENT_ALL_GRID_SPAN_COUNT)"))
|
||||
assertTrue(source.contains("addContentGridItemSpacing(CONTENT_ALL_GRID_SPAN_COUNT)"))
|
||||
assertTrue(source.contains("import androidx.core.view.doOnLayout"))
|
||||
assertTrue(source.contains("doOnLayout { updateAllTabGridItemWidth() }"))
|
||||
assertTrue(source.contains("private fun updateAllTabGridItemWidth()"))
|
||||
assertTrue(source.contains("binding.rvContentAllItems.calculateContentGridItemWidthPx(CONTENT_ALL_GRID_SPAN_COUNT)"))
|
||||
assertTrue(source.contains("MainContentAllType.AUDIO"))
|
||||
assertTrue(source.contains("MainContentAllType.SERIES"))
|
||||
assertTrue(source.contains("MainContentAllType.ORIGINAL"))
|
||||
@@ -358,6 +362,56 @@ class ContentMainFragmentSourceTest {
|
||||
assertEquals(null, banner(link = "mailto:test@example.com").toContentBannerRoute())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `content 전체 탭 adapter는 fixed Small card width 대신 grid item width를 사용한다`() {
|
||||
val fragment = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
|
||||
).readText()
|
||||
val audioAdapter = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ui/ContentAllAudioCardAdapter.kt"
|
||||
).readText()
|
||||
val seriesAdapter = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ui/ContentAllSeriesCardAdapter.kt"
|
||||
).readText()
|
||||
val layoutParams = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ui/ContentRecyclerItemLayoutParams.kt"
|
||||
).readText()
|
||||
val audioCard = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentCardView.kt"
|
||||
).readText()
|
||||
val seriesCard = projectFile(
|
||||
"app/src/main/java/kr/co/vividnext/sodalive/v2/widget/SeriesContentCardView.kt"
|
||||
).readText()
|
||||
|
||||
assertFalse(audioAdapter.contains("setSize(AudioContentCardSize.Small)"))
|
||||
assertFalse(seriesAdapter.contains("setSize(SeriesContentCardSize.Small)"))
|
||||
assertFalse(audioAdapter.contains("holder.itemView.parent as? RecyclerView"))
|
||||
assertFalse(seriesAdapter.contains("holder.itemView.parent as? RecyclerView"))
|
||||
assertSourceContains(audioAdapter, "private var gridItemWidthPx: Int = 0")
|
||||
assertSourceContains(seriesAdapter, "private var gridItemWidthPx: Int = 0")
|
||||
assertSourceContains(audioAdapter, "fun setGridItemWidthPx(widthPx: Int)")
|
||||
assertSourceContains(seriesAdapter, "fun setGridItemWidthPx(widthPx: Int)")
|
||||
assertSourceContains(audioAdapter, "if (widthPx <= 0 || gridItemWidthPx == widthPx) return")
|
||||
assertSourceContains(seriesAdapter, "if (widthPx <= 0 || gridItemWidthPx == widthPx) return")
|
||||
assertSourceContains(fragment, "private fun updateAllTabGridItemWidth()")
|
||||
assertSourceContains(fragment, "binding.rvContentAllItems.calculateContentGridItemWidthPx(CONTENT_ALL_GRID_SPAN_COUNT)")
|
||||
assertSourceContains(fragment, "contentAllAudioCardAdapter.setGridItemWidthPx(widthPx)")
|
||||
assertSourceContains(fragment, "contentAllSeriesCardAdapter.setGridItemWidthPx(widthPx)")
|
||||
assertSourceContains(fragment, "doOnLayout { updateAllTabGridItemWidth() }")
|
||||
assertTrue(
|
||||
"전체 탭 content bind는 item submit 전에 최신 grid width를 adapter에 주입해야 한다.",
|
||||
fragment.indexOf("updateAllTabGridItemWidth()") <
|
||||
fragment.indexOf("contentAllSeriesCardAdapter.submitItems(state.seriesItems)")
|
||||
)
|
||||
assertSourceContains(audioAdapter, "setGridItemWidthPx(gridItemWidthPx)")
|
||||
assertSourceContains(seriesAdapter, "setGridItemWidthPx(gridItemWidthPx)")
|
||||
assertSourceContains(layoutParams, "measuredWidth - paddingLeft - paddingRight")
|
||||
assertSourceContains(layoutParams, "GRID_ITEM_GAP_DP.dpToPx() * (spanCount - 1)")
|
||||
assertSourceContains(audioCard, "fun setGridItemWidthPx(widthPx: Int)")
|
||||
assertSourceContains(seriesCard, "fun setGridItemWidthPx(widthPx: Int)")
|
||||
assertSourceContains(seriesCard, "172f / 122f")
|
||||
}
|
||||
|
||||
private fun banner(
|
||||
eventItem: EventItem? = null,
|
||||
creatorId: Long? = null,
|
||||
|
||||
Reference in New Issue
Block a user