feat(home): 장르 크리에이터 그룹 바인딩을 추가한다
This commit is contained in:
@@ -25,6 +25,7 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentlyAct
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentDebutCreatorSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationUiState
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.RecommendedActivityType
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.visibleHomeGenreCreatorGroups
|
||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeAiCharacterAdapter
|
||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeBannerBinder
|
||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeCheerCreatorAdapter
|
||||
@@ -45,7 +46,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
private val recentDebutCreatorAdapter = HomeRecentDebutCreatorAdapter()
|
||||
private val firstAudioAdapter = HomeFirstAudioAdapter()
|
||||
private val aiCharacterAdapter = HomeAiCharacterAdapter()
|
||||
private val genreCreatorAdapter = HomeGenreCreatorAdapter()
|
||||
private val genreCreatorAdapter = HomeGenreCreatorAdapter { creatorIds -> onGenreFollowAllClick(creatorIds) }
|
||||
private val cheerCreatorAdapter = HomeCheerCreatorAdapter()
|
||||
private var bannerBinder: HomeBannerBinder? = null
|
||||
private var onGenreFollowAllClick: (List<Long>) -> Unit = {}
|
||||
@@ -127,20 +128,9 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
}
|
||||
|
||||
private fun bindGenreCreatorSection(section: HomeRecommendationGenreCreatorSection) {
|
||||
val group = section.groups.firstOrNull { it.creators.isNotEmpty() }
|
||||
binding.llHomeGenreCreatorSection.visibility = if (group == null) View.GONE else View.VISIBLE
|
||||
if (group == null) {
|
||||
genreCreatorAdapter.submitItems(emptyList())
|
||||
return
|
||||
}
|
||||
binding.tvHomeGenreCreatorTitleGenre.text = group.genre
|
||||
genreCreatorAdapter.submitItems(group.creators)
|
||||
HomeFollowAllButtonBinder.bind(
|
||||
view = binding.viewHomeGenreFollowAll.root,
|
||||
creatorIds = group.creators.map { it.creatorId },
|
||||
isFollowCompleted = group.isFollowCompleted,
|
||||
onClick = onGenreFollowAllClick
|
||||
)
|
||||
val visibleGroups = section.visibleHomeGenreCreatorGroups()
|
||||
binding.llHomeGenreCreatorSection.visibility = visibleGroups.toSectionVisibility()
|
||||
genreCreatorAdapter.submitGroups(visibleGroups)
|
||||
}
|
||||
|
||||
private fun bindCheerCreatorSection(section: HomeRecommendationCheerCreatorSection) {
|
||||
@@ -252,15 +242,18 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
)
|
||||
),
|
||||
genreCreators = HomeRecommendationGenreCreatorSection(
|
||||
listOf(HomeRecommendationGenreCreatorGroupUiModel("로맨스", sampleCreators(601L)))
|
||||
listOf(
|
||||
HomeRecommendationGenreCreatorGroupUiModel("로맨스", sampleCreators(601L, count = 8)),
|
||||
HomeRecommendationGenreCreatorGroupUiModel("판타지", sampleCreators(701L, count = 8))
|
||||
)
|
||||
),
|
||||
cheerCreators = HomeRecommendationCheerCreatorSection(sampleCreators(701L)),
|
||||
popularCommunityPosts = HomeRecommendationPopularCommunityPostSection(emptyList())
|
||||
)
|
||||
|
||||
private fun sampleCreators(startId: Long): List<HomeRecommendationCreatorUiModel> = listOf(
|
||||
HomeRecommendationCreatorUiModel(startId, "아린", null),
|
||||
HomeRecommendationCreatorUiModel(startId + 1, "유나", null),
|
||||
HomeRecommendationCreatorUiModel(startId + 2, "세이", null)
|
||||
)
|
||||
private fun sampleCreators(startId: Long, count: Int = 3): List<HomeRecommendationCreatorUiModel> {
|
||||
return List(count) { index ->
|
||||
HomeRecommendationCreatorUiModel(startId + index, "크리에이터${index + 1}", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ data class HomeRecommendationGenreCreatorSection(
|
||||
val groups: List<HomeRecommendationGenreCreatorGroupUiModel>
|
||||
)
|
||||
|
||||
fun HomeRecommendationGenreCreatorSection.visibleHomeGenreCreatorGroups(): List<HomeRecommendationGenreCreatorGroupUiModel> {
|
||||
return groups.filter { it.creators.isNotEmpty() }.take(HOME_GENRE_CREATOR_MAX_GROUP_COUNT)
|
||||
}
|
||||
|
||||
data class HomeRecommendationCheerCreatorSection(
|
||||
val items: List<HomeRecommendationCreatorUiModel>,
|
||||
val isFollowCompleted: Boolean = false
|
||||
@@ -110,3 +114,5 @@ sealed interface HomeRecommendationPaidStatus {
|
||||
val price: Int
|
||||
) : HomeRecommendationPaidStatus
|
||||
}
|
||||
|
||||
private const val HOME_GENRE_CREATOR_MAX_GROUP_COUNT = 5
|
||||
|
||||
@@ -1,3 +1,137 @@
|
||||
package kr.co.vividnext.sodalive.v2.main.home.ui
|
||||
|
||||
class HomeGenreCreatorAdapter : HomeCreatorProfileAdapter()
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.GridLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationCreatorUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationGenreCreatorGroupUiModel
|
||||
|
||||
class HomeGenreCreatorAdapter(
|
||||
private val onFollowAllClick: (List<Long>) -> Unit
|
||||
) : RecyclerView.Adapter<HomeGenreCreatorAdapter.GenreCreatorGroupViewHolder>() {
|
||||
private var groups: List<HomeRecommendationGenreCreatorGroupUiModel> = emptyList()
|
||||
|
||||
fun submitGroups(groups: List<HomeRecommendationGenreCreatorGroupUiModel>) {
|
||||
this.groups = groups.filter { it.creators.isNotEmpty() }.take(MAX_GROUP_COUNT)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenreCreatorGroupViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_home_genre_creator_group, parent, false)
|
||||
view.layoutParams = genreGroupLayoutParams(parent)
|
||||
return GenreCreatorGroupViewHolder(view, parent, onFollowAllClick)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: GenreCreatorGroupViewHolder, position: Int) {
|
||||
holder.bind(groups[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = groups.size
|
||||
|
||||
class GenreCreatorGroupViewHolder(
|
||||
itemView: View,
|
||||
private val parent: ViewGroup,
|
||||
private val onFollowAllClick: (List<Long>) -> Unit
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
private val genreText = itemView.findViewById<TextView>(R.id.tv_home_genre_creator_group_title_genre)
|
||||
private val creatorGrid = itemView.findViewById<GridLayout>(R.id.gl_home_genre_creator_profiles)
|
||||
private val followAllButton = itemView.findViewById<View>(R.id.view_home_genre_group_follow_all)
|
||||
|
||||
fun bind(group: HomeRecommendationGenreCreatorGroupUiModel) {
|
||||
genreText.text = group.genre
|
||||
val creators = group.creators.take(CREATOR_COUNT_PER_GROUP)
|
||||
bindCreators(creators, calculateProfileSize())
|
||||
creatorGrid.post {
|
||||
bindCreators(creators, calculateProfileSize())
|
||||
}
|
||||
HomeFollowAllButtonBinder.bind(
|
||||
view = followAllButton,
|
||||
creatorIds = group.creators.map { it.creatorId },
|
||||
isFollowCompleted = group.isFollowCompleted,
|
||||
onClick = onFollowAllClick
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateProfileSize(): Int {
|
||||
val gridWidth = creatorGrid.width.takeIf { it > 0 }
|
||||
?: (resolvedCardWidth() - itemView.paddingStart - itemView.paddingEnd)
|
||||
val totalColumnGap = itemView.resources.getDimensionPixelSize(R.dimen.spacing_14) * (GENRE_GRID_COLUMN_COUNT - 1)
|
||||
return (gridWidth - totalColumnGap) / GENRE_GRID_COLUMN_COUNT
|
||||
}
|
||||
|
||||
private fun resolvedCardWidth(): Int {
|
||||
val parentContentWidth = parentContentWidth()
|
||||
itemView.width.takeIf { it > 0 }?.let { return minOf(it, parentContentWidth) }
|
||||
itemView.layoutParams.width.takeIf { it > 0 }?.let { return minOf(it, parentContentWidth) }
|
||||
|
||||
parentContentWidth.takeIf { it > 0 }?.let { return it }
|
||||
|
||||
return itemView.resources.displayMetrics.widthPixels - parentHorizontalInset()
|
||||
}
|
||||
|
||||
private fun parentContentWidth(): Int {
|
||||
return parent.width - parentHorizontalInset()
|
||||
}
|
||||
|
||||
private fun parentHorizontalInset(): Int {
|
||||
val paddingInset = parent.paddingStart + parent.paddingEnd
|
||||
return paddingInset.takeIf { it > 0 } ?: itemView.resources.getDimensionPixelSize(R.dimen.spacing_28)
|
||||
}
|
||||
|
||||
private fun bindCreators(creators: List<HomeRecommendationCreatorUiModel>, profileSize: Int) {
|
||||
creatorGrid.removeAllViews()
|
||||
creators.forEachIndexed { index, creator ->
|
||||
val profileView = LayoutInflater.from(creatorGrid.context).inflate(
|
||||
R.layout.item_home_genre_creator_profile,
|
||||
creatorGrid,
|
||||
false
|
||||
)
|
||||
val row = index / GENRE_GRID_COLUMN_COUNT
|
||||
val column = index % GENRE_GRID_COLUMN_COUNT
|
||||
profileView.layoutParams = GridLayout.LayoutParams(
|
||||
GridLayout.spec(row),
|
||||
GridLayout.spec(column)
|
||||
).apply {
|
||||
width = profileSize
|
||||
height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
if (column < GENRE_GRID_COLUMN_COUNT - 1) {
|
||||
marginEnd = profileView.resources.getDimensionPixelSize(R.dimen.spacing_14)
|
||||
}
|
||||
if (row > 0) {
|
||||
topMargin = profileView.resources.getDimensionPixelSize(R.dimen.spacing_14)
|
||||
}
|
||||
}
|
||||
profileView.findViewById<ImageView>(R.id.iv_home_genre_creator_profile).apply {
|
||||
if (creator.profileImage == null) {
|
||||
setImageDrawable(null)
|
||||
} else {
|
||||
loadUrl(creator.profileImage)
|
||||
}
|
||||
}
|
||||
profileView.findViewById<TextView>(R.id.tv_home_genre_creator_profile_nickname).text = creator.nickname
|
||||
creatorGrid.addView(profileView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_GROUP_COUNT = 5
|
||||
private const val CREATOR_COUNT_PER_GROUP = 8
|
||||
private const val GENRE_GRID_COLUMN_COUNT = 4
|
||||
}
|
||||
}
|
||||
|
||||
private fun genreGroupLayoutParams(parent: ViewGroup): RecyclerView.LayoutParams {
|
||||
val paddingInset = parent.paddingStart + parent.paddingEnd
|
||||
val horizontalInset = paddingInset.takeIf { it > 0 } ?: parent.resources.getDimensionPixelSize(R.dimen.spacing_28)
|
||||
val width = (parent.width - horizontalInset).takeIf { it > 0 } ?: ViewGroup.LayoutParams.MATCH_PARENT
|
||||
return RecyclerView.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
marginEnd = parent.resources.getDimensionPixelSize(R.dimen.spacing_4)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user