fix(home): 팔로잉 탭 후속 UI를 보정한다

This commit is contained in:
2026-06-26 15:42:38 +09:00
parent 50b45d04e5
commit 595bc50cde
6 changed files with 167 additions and 95 deletions

View File

@@ -196,7 +196,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
adapter = followingLiveAdapter adapter = followingLiveAdapter
} }
binding.rvHomeFollowingRecentChats.apply { binding.rvHomeFollowingRecentChats.apply {
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
adapter = followingChatAdapter adapter = followingChatAdapter
} }
binding.rvHomeFollowingMonthlySchedules.apply { binding.rvHomeFollowingMonthlySchedules.apply {
@@ -448,10 +448,6 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
binding.viewHomePopularCommunityTitle.setTitle( binding.viewHomePopularCommunityTitle.setTitle(
R.string.home_recommendation_section_popular_community_posts R.string.home_recommendation_section_popular_community_posts
) )
binding.viewHomeFollowingCreatorsTitle.setTitle(
R.string.screen_home_following_creators_title,
showMore = true
)
binding.viewHomeFollowingOnAirTitle.setTitle( binding.viewHomeFollowingOnAirTitle.setTitle(
R.string.screen_home_following_on_air, R.string.screen_home_following_on_air,
showMore = true showMore = true
@@ -465,12 +461,8 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
showMore = true showMore = true
) )
binding.viewHomeFollowingRecentNewsTitle.setTitle( binding.viewHomeFollowingRecentNewsTitle.setTitle(
R.string.screen_home_following_recent_news_title, R.string.screen_home_following_recent_news_title
showMore = true
) )
binding.viewHomeFollowingCreatorsTitle.ivSectionTitleChevron.setOnClickListener {
onFollowingSectionMoreClick(HomeFollowingSection.CREATORS)
}
binding.viewHomeFollowingOnAirTitle.ivSectionTitleChevron.setOnClickListener { binding.viewHomeFollowingOnAirTitle.ivSectionTitleChevron.setOnClickListener {
onFollowingSectionMoreClick(HomeFollowingSection.ON_AIR) onFollowingSectionMoreClick(HomeFollowingSection.ON_AIR)
} }
@@ -480,9 +472,6 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
binding.viewHomeFollowingMonthlySchedulesTitle.ivSectionTitleChevron.setOnClickListener { binding.viewHomeFollowingMonthlySchedulesTitle.ivSectionTitleChevron.setOnClickListener {
onFollowingSectionMoreClick(HomeFollowingSection.MONTHLY_SCHEDULES) onFollowingSectionMoreClick(HomeFollowingSection.MONTHLY_SCHEDULES)
} }
binding.viewHomeFollowingRecentNewsTitle.ivSectionTitleChevron.setOnClickListener {
onFollowingSectionMoreClick(HomeFollowingSection.RECENT_NEWS)
}
} }
private fun ViewSectionTitleBinding.setTitle( private fun ViewSectionTitleBinding.setTitle(

View File

@@ -22,10 +22,7 @@ class HomeFollowingChatAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_home_following_chat, parent, false) val view = LayoutInflater.from(parent.context).inflate(R.layout.item_home_following_chat, parent, false)
view.layoutParams = RecyclerView.LayoutParams( view.layoutParams = recyclerItemLayoutParams(parent)
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply { bottomMargin = parent.resources.getDimensionPixelSize(R.dimen.spacing_8) }
return ChatViewHolder(view, onClickItem) return ChatViewHolder(view, onClickItem)
} }
@@ -43,12 +40,14 @@ class HomeFollowingChatAdapter(
private val nicknameText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_creator_nickname) private val nicknameText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_creator_nickname)
private val messageText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_message) private val messageText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_message)
private val timeText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_time) private val timeText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_time)
private val directBadgeText = itemView.findViewById<TextView>(R.id.tv_home_following_chat_direct_badge)
fun bind(item: ChatRoomListUiItem) { fun bind(item: ChatRoomListUiItem) {
profileImage.loadHomeCreatorProfileImage(item.targetImageUrl) profileImage.loadHomeCreatorProfileImage(item.targetImageUrl)
nicknameText.text = item.targetName nicknameText.text = item.targetName
messageText.text = item.lastMessage messageText.text = item.lastMessage
timeText.text = formatChatRoomLastMessageTime(itemView.context, item.lastMessageAt) timeText.text = formatChatRoomLastMessageTime(itemView.context, item.lastMessageAt)
directBadgeText.visibility = if (item.showDirectBadge) View.VISIBLE else View.GONE
itemView.setOnClickListener { onClickItem(item) } itemView.setOnClickListener { onClickItem(item) }
} }
} }

View File

@@ -3,13 +3,19 @@ package kr.co.vividnext.sodalive.v2.main.home.ui
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.extensions.loadUrl import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.v2.main.home.data.FollowingNewsType
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingNewsUiItem import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingNewsUiItem
import kr.co.vividnext.sodalive.v2.widget.feed.FeedContentView
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
import kr.co.vividnext.sodalive.v2.widget.feed.FeedRankView
import kr.co.vividnext.sodalive.v2.widget.feed.FeedSize
import kr.co.vividnext.sodalive.v2.widget.feed.FeedVariant
import kr.co.vividnext.sodalive.v2.widget.feed.FeedWidthMode
import kr.co.vividnext.sodalive.v2.widget.feed.FeedCommunityView
import kotlin.math.roundToInt
class HomeFollowingNewsAdapter( class HomeFollowingNewsAdapter(
private val onClickItem: (HomeFollowingNewsUiItem) -> Unit = {} private val onClickItem: (HomeFollowingNewsUiItem) -> Unit = {}
@@ -21,26 +27,34 @@ class HomeFollowingNewsAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
override fun getItemViewType(position: Int): Int = when (items[position]) { override fun getItemViewType(position: Int): Int = when (val item = items[position]) {
is HomeFollowingNewsUiItem.Ranking -> VIEW_TYPE_RANKING is HomeFollowingNewsUiItem.Ranking -> VIEW_TYPE_RANKING
is HomeFollowingNewsUiItem.Content -> VIEW_TYPE_CONTENT is HomeFollowingNewsUiItem.Content -> when (item.type) {
FollowingNewsType.COMMUNITY_POST -> VIEW_TYPE_COMMUNITY
FollowingNewsType.AUDIO_CONTENT,
FollowingNewsType.PHOTO_CONTENT -> VIEW_TYPE_CONTENT
FollowingNewsType.CREATOR_RANKING,
FollowingNewsType.CONTENT_RANKING -> VIEW_TYPE_CONTENT
}
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
val layoutResId = if (viewType == VIEW_TYPE_RANKING) { val layoutResId = when (viewType) {
R.layout.item_home_following_news_rank VIEW_TYPE_RANKING -> R.layout.view_feed_rank
} else { VIEW_TYPE_COMMUNITY -> R.layout.view_feed_community
R.layout.item_home_following_news_content VIEW_TYPE_CONTENT -> R.layout.view_feed_content
else -> error("Unknown viewType: $viewType")
} }
val view = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false) val view = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
view.layoutParams = RecyclerView.LayoutParams( view.layoutParams = RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
).apply { bottomMargin = parent.resources.getDimensionPixelSize(R.dimen.spacing_8) } ).apply { bottomMargin = parent.resources.getDimensionPixelSize(R.dimen.spacing_8) }
return if (viewType == VIEW_TYPE_RANKING) { return when (viewType) {
RankingViewHolder(view, onClickItem) VIEW_TYPE_RANKING -> RankingViewHolder(view as FeedRankView, parent, onClickItem)
} else { VIEW_TYPE_COMMUNITY -> CommunityViewHolder(view as FeedCommunityView, parent, onClickItem)
ContentViewHolder(view, onClickItem) VIEW_TYPE_CONTENT -> ContentViewHolder(view as FeedContentView, parent, onClickItem)
else -> error("Unknown viewType: $viewType")
} }
} }
@@ -55,50 +69,100 @@ class HomeFollowingNewsAdapter(
} }
class RankingViewHolder( class RankingViewHolder(
itemView: View, private val view: FeedRankView,
private val parent: ViewGroup,
private val onClickItem: (HomeFollowingNewsUiItem) -> Unit private val onClickItem: (HomeFollowingNewsUiItem) -> Unit
) : NewsViewHolder(itemView) { ) : NewsViewHolder(view) {
private val rankText = itemView.findViewById<TextView>(R.id.tv_home_following_news_rank)
private val titleText = itemView.findViewById<TextView>(R.id.tv_home_following_news_rank_title)
private val countText = itemView.findViewById<TextView>(R.id.tv_home_following_news_rank_count)
override fun bind(item: HomeFollowingNewsUiItem) { override fun bind(item: HomeFollowingNewsUiItem) {
val ranking = item as HomeFollowingNewsUiItem.Ranking val ranking = item as HomeFollowingNewsUiItem.Ranking
rankText.text = ranking.rank.toString() val feedItem = FeedItem.Rank(
titleText.text = ranking.title feedId = ranking.newsId,
countText.text = ranking.body imageUrl = ranking.thumbnailImageUrl.orEmpty(),
itemView.setOnClickListener { onClickItem(ranking) } rankText = ranking.rank.toString(),
message = ranking.body.ifBlank { ranking.title },
highlightRanges = emptyList()
)
view.setFeedSize(feedSize(FeedVariant.Rank, parent))
view.bind(feedItem)
bindImage(view.imageView(), feedItem.imageUrl)
view.setOnFeedClick { onClickItem(ranking) }
}
}
class CommunityViewHolder(
private val view: FeedCommunityView,
private val parent: ViewGroup,
private val onClickItem: (HomeFollowingNewsUiItem) -> Unit
) : NewsViewHolder(view) {
override fun bind(item: HomeFollowingNewsUiItem) {
val community = item as HomeFollowingNewsUiItem.Content
val feedItem = FeedItem.Community(
feedId = community.newsId,
creatorId = community.targetId.toString(),
creatorName = community.creatorNickname,
creatorImageUrl = community.creatorProfileImageUrl,
postId = community.targetId.toString(),
bodyText = community.body.ifBlank { community.title },
keywordText = "",
createdAtText = community.visibleFromText,
commentCount = 0,
likeCount = 0,
imageUrl = community.thumbnailImageUrl
)
view.setFeedSize(feedSize(FeedVariant.Community, parent))
view.bind(feedItem)
view.profileImageView().loadHomeCreatorProfileImage(feedItem.creatorImageUrl)
bindImage(view.communityImageView(), feedItem.imageUrl.orEmpty())
view.setOnFeedClick { onClickItem(community) }
} }
} }
class ContentViewHolder( class ContentViewHolder(
itemView: View, private val view: FeedContentView,
private val parent: ViewGroup,
private val onClickItem: (HomeFollowingNewsUiItem) -> Unit private val onClickItem: (HomeFollowingNewsUiItem) -> Unit
) : NewsViewHolder(itemView) { ) : NewsViewHolder(view) {
private val profileImage = itemView.findViewById<ImageView>(R.id.iv_home_following_news_creator_profile)
private val nicknameText = itemView.findViewById<TextView>(R.id.tv_home_following_news_creator_nickname)
private val createdAtText = itemView.findViewById<TextView>(R.id.tv_home_following_news_created_at)
private val labelText = itemView.findViewById<TextView>(R.id.tv_home_following_news_label)
private val titleText = itemView.findViewById<TextView>(R.id.tv_home_following_news_title)
private val contentText = itemView.findViewById<TextView>(R.id.tv_home_following_news_content)
private val thumbnailImage = itemView.findViewById<ImageView>(R.id.iv_home_following_news_thumbnail)
override fun bind(item: HomeFollowingNewsUiItem) { override fun bind(item: HomeFollowingNewsUiItem) {
val content = item as HomeFollowingNewsUiItem.Content val content = item as HomeFollowingNewsUiItem.Content
profileImage.loadHomeCreatorProfileImage(content.creatorProfileImageUrl) val feedItem = FeedItem.Content(
nicknameText.text = content.creatorNickname feedId = content.newsId,
createdAtText.text = content.visibleFromText creatorId = content.targetId.toString(),
labelText.setText(content.labelResId) creatorName = content.creatorNickname,
titleText.text = content.title creatorImageUrl = content.creatorProfileImageUrl,
contentText.text = content.body contentId = content.targetId.toString(),
thumbnailImage.isVisible = !content.thumbnailImageUrl.isNullOrBlank() contentTitle = content.title,
content.thumbnailImageUrl?.let(thumbnailImage::loadUrl) contentImageUrl = content.thumbnailImageUrl.orEmpty(),
itemView.setOnClickListener { onClickItem(content) } createdAtText = content.visibleFromText
)
view.setFeedSize(feedSize(FeedVariant.Content, parent))
view.bind(feedItem)
bindImage(view.profileImageView(), feedItem.creatorImageUrl)
bindImage(view.contentImageView(), feedItem.contentImageUrl)
view.setOnFeedClick { onClickItem(content) }
} }
} }
companion object { companion object {
private const val VIEW_TYPE_RANKING = 0 private const val VIEW_TYPE_RANKING = 0
private const val VIEW_TYPE_CONTENT = 1 private const val VIEW_TYPE_CONTENT = 1
private const val VIEW_TYPE_COMMUNITY = 2
private fun feedSize(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, FeedWidthMode.ParentAvailable, availableWidthDp)
}
private fun bindImage(imageView: android.widget.ImageView, url: String) {
if (url.isBlank()) {
imageView.setImageDrawable(null)
} else {
imageView.loadUrl(url)
}
}
} }
} }

View File

@@ -264,15 +264,10 @@
android:layout_marginTop="@dimen/spacing_12" android:layout_marginTop="@dimen/spacing_12"
android:orientation="vertical"> android:orientation="vertical">
<include
android:id="@+id/view_home_following_creators_title"
layout="@layout/view_section_title" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_home_following_creators" android:id="@+id/rv_home_following_creators"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_14"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingHorizontal="@dimen/spacing_14" android:paddingHorizontal="@dimen/spacing_14"
@@ -320,7 +315,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_14" android:layout_marginTop="@dimen/spacing_14"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical" android:orientation="horizontal"
android:paddingHorizontal="@dimen/spacing_14" android:paddingHorizontal="@dimen/spacing_14"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_home_following_chat" /> tools:listitem="@layout/item_home_following_chat" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="284dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/bg_feed_card" android:background="@drawable/bg_feed_card"
android:gravity="center_vertical" android:gravity="center_vertical"
@@ -10,30 +10,53 @@
<ImageView <ImageView
android:id="@+id/iv_home_following_chat_creator_profile" android:id="@+id/iv_home_following_chat_creator_profile"
android:layout_width="42dp" android:layout_width="62dp"
android:layout_height="42dp" android:layout_height="62dp"
android:background="@drawable/bg_round_corner_999_263238" android:background="@drawable/bg_round_corner_999_263238"
android:contentDescription="@null" android:contentDescription="@null"
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:src="@drawable/ic_placeholder_profile" /> tools:src="@drawable/ic_placeholder_profile" />
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="170dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_12" android:layout_marginStart="@dimen/spacing_14"
android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<TextView <LinearLayout
android:id="@+id/tv_home_following_chat_creator_nickname"
style="@style/Typography.Body4"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="17dp"
android:ellipsize="end" android:gravity="center_vertical"
android:includeFontPadding="false" android:orientation="horizontal">
android:maxLines="1"
android:textColor="@color/white" <TextView
tools:text="크리에이터 이름" /> android:id="@+id/tv_home_following_chat_direct_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_chat_direct_badge"
android:fontFamily="@font/pattaya_regular"
android:includeFontPadding="false"
android:paddingHorizontal="@dimen/spacing_4"
android:text="@string/screen_chat_direct_badge"
android:textColor="@color/white"
android:textSize="12sp"
android:visibility="gone"
tools:visibility="visible" />
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<TextView
android:id="@+id/tv_home_following_chat_time"
style="@style/Typography.Caption3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textColor="@color/gray_500"
tools:text="4분전" />
</LinearLayout>
<TextView <TextView
android:id="@+id/tv_home_following_chat_message" android:id="@+id/tv_home_following_chat_message"
@@ -43,18 +66,20 @@
android:layout_marginTop="@dimen/spacing_6" android:layout_marginTop="@dimen/spacing_6"
android:ellipsize="end" android:ellipsize="end"
android:includeFontPadding="false" android:includeFontPadding="false"
android:maxLines="2" android:maxLines="1"
android:textColor="@color/gray_300" android:textColor="@color/white"
tools:text="최근 대화 내용이 두 줄까지 노출됩니다." /> tools:text="마지막 대화 내용이 들어가는 부분입니다. 한 줄 넘어가는경우 말줄임" />
</LinearLayout>
<TextView <TextView
android:id="@+id/tv_home_following_chat_time" android:id="@+id/tv_home_following_chat_creator_nickname"
style="@style/Typography.Body6" style="@style/Typography.Caption3"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_8" android:layout_marginTop="@dimen/spacing_4"
android:includeFontPadding="false" android:ellipsize="end"
android:textColor="@color/gray_500" android:includeFontPadding="false"
tools:text="3분 전" /> android:maxLines="1"
android:textColor="@color/gray_500"
tools:text="크리에이터 이름" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="76dp" android:layout_width="75dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/iv_home_following_creator_profile" android:id="@+id/iv_home_following_creator_profile"
android:layout_width="64dp" android:layout_width="75dp"
android:layout_height="64dp" android:layout_height="75dp"
android:background="@drawable/bg_round_corner_999_263238" android:background="@drawable/bg_round_corner_999_263238"
android:contentDescription="@null" android:contentDescription="@null"
android:scaleType="centerCrop" android:scaleType="centerCrop"
@@ -20,7 +20,7 @@
style="@style/Typography.Body5" style="@style/Typography.Body5"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_8" android:layout_marginTop="@dimen/spacing_6"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:includeFontPadding="false" android:includeFontPadding="false"