feat(feed): 피드 어댑터와 뷰 테스트를 추가한다

This commit is contained in:
2026-05-21 15:53:40 +09:00
parent 59ea5de00a
commit a8e0f2377d
2 changed files with 244 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
package kr.co.vividnext.sodalive.v2.widget.feed
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kotlin.math.roundToInt
class FeedAdapter(
private val widthMode: FeedWidthMode = FeedWidthMode.FigmaFixed,
private val horizontalItemDecorationDp: Int = 0,
private val onClickItem: (FeedItem) -> Unit,
private val onBindImages: (FeedImageViews, FeedItem) -> Unit = { _, _ -> }
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val items = mutableListOf<FeedItem>()
override fun getItemViewType(position: Int): Int = when (items[position].variant) {
FeedVariant.Rank -> VIEW_TYPE_RANK
FeedVariant.Live -> VIEW_TYPE_LIVE
FeedVariant.Content -> VIEW_TYPE_CONTENT
FeedVariant.Community -> VIEW_TYPE_COMMUNITY
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
VIEW_TYPE_RANK -> RankViewHolder(inflater.inflate(R.layout.view_feed_rank, parent, false) as FeedRankView, parent)
VIEW_TYPE_LIVE -> LiveViewHolder(inflater.inflate(R.layout.view_feed_live, parent, false) as FeedLiveView, parent)
VIEW_TYPE_CONTENT -> ContentViewHolder(inflater.inflate(R.layout.view_feed_content, parent, false) as FeedContentView, parent)
VIEW_TYPE_COMMUNITY -> CommunityViewHolder(inflater.inflate(R.layout.view_feed_community, parent, false) as FeedCommunityView, parent)
else -> error("Unknown viewType: $viewType")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = items[position]) {
is FeedItem.Rank -> (holder as RankViewHolder).bind(item)
is FeedItem.Live -> (holder as LiveViewHolder).bind(item)
is FeedItem.Content -> (holder as ContentViewHolder).bind(item)
is FeedItem.Community -> (holder as CommunityViewHolder).bind(item)
}
}
override fun getItemCount(): Int = items.size
fun submitItems(items: List<FeedItem>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
private inner class RankViewHolder(
private val view: FeedRankView,
private val parent: ViewGroup
) : RecyclerView.ViewHolder(view) {
fun bind(item: FeedItem.Rank) {
view.setFeedSize(calculateSize(item.variant, parent))
view.bind(item)
view.setOnFeedClick(onClickItem)
onBindImages(FeedImageViews(primary = view.imageView()), item)
}
}
private inner class LiveViewHolder(
private val view: FeedLiveView,
private val parent: ViewGroup
) : RecyclerView.ViewHolder(view) {
fun bind(item: FeedItem.Live) {
view.setFeedSize(calculateSize(item.variant, parent))
view.bind(item)
view.setOnFeedClick(onClickItem)
onBindImages(FeedImageViews(profile = view.profileImageView()), item)
}
}
private inner class ContentViewHolder(
private val view: FeedContentView,
private val parent: ViewGroup
) : RecyclerView.ViewHolder(view) {
fun bind(item: FeedItem.Content) {
view.setFeedSize(calculateSize(item.variant, parent))
view.bind(item)
view.setOnFeedClick(onClickItem)
onBindImages(FeedImageViews(primary = view.contentImageView(), profile = view.profileImageView()), item)
}
}
private inner class CommunityViewHolder(
private val view: FeedCommunityView,
private val parent: ViewGroup
) : RecyclerView.ViewHolder(view) {
fun bind(item: FeedItem.Community) {
view.setFeedSize(calculateSize(item.variant, parent))
view.bind(item)
view.setOnFeedClick(onClickItem)
onBindImages(FeedImageViews(profile = view.profileImageView()), item)
}
}
private fun calculateSize(variant: FeedVariant, parent: ViewGroup): FeedSize {
val parentWidthPx = parent.width.takeIf { it > 0 } ?: parent.resources.displayMetrics.widthPixels
val availableWidthPx = parentWidthPx - parent.paddingLeft - parent.paddingRight
val availableWidthDp = (availableWidthPx / parent.resources.displayMetrics.density).roundToInt()
return FeedSize.from(variant, widthMode, availableWidthDp, horizontalItemDecorationDp)
}
companion object {
private const val VIEW_TYPE_RANK = 1
private const val VIEW_TYPE_LIVE = 2
private const val VIEW_TYPE_CONTENT = 3
private const val VIEW_TYPE_COMMUNITY = 4
}
}
data class FeedImageViews(
val primary: ImageView? = null,
val profile: ImageView? = null
)