From db2e3bc8f26e56107282c2792ff6034c269f0f09 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 15 Jul 2025 20:20:54 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-=20?= =?UTF-8?q?=EC=B6=94=EC=B2=9C=20=EC=B1=84=EB=84=90=20UI=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- .../vividnext/sodalive/home/HomeFragment.kt | 78 ++++++ .../home/HomeRecommendChannelAdapter.kt | 105 +++++++ .../res/drawable-mdpi/ic_comment_white.png | Bin 0 -> 437 bytes .../main/res/drawable-mdpi/ic_heart_white.png | Bin 0 -> 393 bytes .../drawable/bg_home_recommend_channel.xml | 13 + .../layout/item_home_recommend_channel.xml | 260 ++++++++++++++++++ 7 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/home/HomeRecommendChannelAdapter.kt create mode 100644 app/src/main/res/drawable-mdpi/ic_comment_white.png create mode 100644 app/src/main/res/drawable-mdpi/ic_heart_white.png create mode 100644 app/src/main/res/drawable/bg_home_recommend_channel.xml create mode 100644 app/src/main/res/layout/item_home_recommend_channel.xml diff --git a/app/build.gradle b/app/build.gradle index 22346c8c..f958aa90 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { minSdk 23 targetSdk 34 versionCode 174 - versionName "1.38.0" + versionName "1.39.0" } buildTypes { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt index e7001407..1701a341 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt @@ -72,6 +72,7 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl private lateinit var auditionAdapter: AuditionBannerAdapter private lateinit var seriesDayOfWeekAdapter: HomeSeriesAdapter private lateinit var weelyChartAdapter: HomeWeeklyChartAdapter + private lateinit var recommendChannelAdapter: HomeRecommendChannelAdapter private lateinit var homeFreeContentAdapter: HomeContentAdapter private lateinit var curationAdapter: HomeCurationAdapter @@ -167,6 +168,7 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl setupAudition() setupSeriesDayOfWeek() setupWeelyChart() + setupRecommendChannel() setupFreeContent() setupCuration() } @@ -860,6 +862,82 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl } } + private fun setupRecommendChannel() { + val spSectionTitle = SpannableString(binding.tvRecommendChannel.text) + spSectionTitle.setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + requireContext(), + R.color.color_3bb9f1 + ) + ), + 0, + 2, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + binding.tvRecommendChannel.text = spSectionTitle + + recommendChannelAdapter = HomeRecommendChannelAdapter( + onClickCreatorProfile = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + }, + onClickContentItem = { + startActivity( + Intent(requireContext(), AudioContentDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) + } + ) + } + ) + + val recyclerView = binding.rvRecommendChannel + recyclerView.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + recommendChannelAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + recyclerView.adapter = recommendChannelAdapter + + viewModel.recommendChannelListLiveData.observe(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.llRecommendChannel.visibility = View.VISIBLE + recommendChannelAdapter.addItems(it) + } else { + binding.llRecommendChannel.visibility = View.GONE + } + } + } + private fun setupFreeContent() { val spSectionTitle = SpannableString(binding.tvFreeContent.text) spSectionTitle.setSpan( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRecommendChannelAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRecommendChannelAdapter.kt new file mode 100644 index 00000000..b3779e34 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRecommendChannelAdapter.kt @@ -0,0 +1,105 @@ +package kr.co.vividnext.sodalive.home + +import android.annotation.SuppressLint +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.CircleCropTransformation +import coil.transform.RoundedCornersTransformation +import kr.co.vividnext.sodalive.databinding.ItemHomeRecommendChannelBinding +import kr.co.vividnext.sodalive.extensions.moneyFormat + +class HomeRecommendChannelAdapter( + private val onClickCreatorProfile: (Long) -> Unit, + private val onClickContentItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + private var items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemHomeRecommendChannelBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: RecommendChannelResponse) { + binding.ivCreatorProfile.load(item.creatorProfileImageUrl) { + crossfade(true) + placeholder(android.R.drawable.ic_menu_gallery) + transformations(CircleCropTransformation()) + } + + binding.tvNickname.text = item.creatorNickname + binding.tvContentCount.text = item.contentCount.moneyFormat() + + binding.ivCreatorProfile.setOnClickListener { onClickCreatorProfile(item.channelId) } + binding.llCreatorProfile.setOnClickListener { onClickCreatorProfile(item.channelId) } + + item.contentList.forEachIndexed { index, content -> + if (index >= 3) return@forEachIndexed + + when (index) { + 0 -> { + binding.ivCover1.load(content.thumbnailImageUrl) { + crossfade(true) + placeholder(android.R.drawable.ic_menu_gallery) + transformations(RoundedCornersTransformation(12f)) + } + + binding.tvTitle1.text = content.title + binding.tvLikeCount1.text = content.likeCount.moneyFormat() + binding.tvCommentCount1.text = content.commentCount.moneyFormat() + + binding.llContent1.setOnClickListener { onClickContentItem(content.contentId) } + } + + 1 -> { + binding.ivCover2.load(content.thumbnailImageUrl) { + crossfade(true) + placeholder(android.R.drawable.ic_menu_gallery) + transformations(RoundedCornersTransformation(12f)) + } + + binding.tvTitle2.text = content.title + binding.tvLikeCount2.text = content.likeCount.moneyFormat() + binding.tvCommentCount2.text = content.commentCount.moneyFormat() + + binding.llContent2.setOnClickListener { onClickContentItem(content.contentId) } + } + + 2 -> { + binding.ivCover3.load(content.thumbnailImageUrl) { + crossfade(true) + placeholder(android.R.drawable.ic_menu_gallery) + transformations(RoundedCornersTransformation(12f)) + } + + binding.tvTitle3.text = content.title + binding.tvLikeCount3.text = content.likeCount.moneyFormat() + binding.tvCommentCount3.text = content.commentCount.moneyFormat() + + binding.llContent3.setOnClickListener { onClickContentItem(content.contentId) } + } + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemHomeRecommendChannelBinding.inflate( + android.view.LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.size + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.clear() + this.items.addAll(items) + notifyDataSetChanged() + } +} diff --git a/app/src/main/res/drawable-mdpi/ic_comment_white.png b/app/src/main/res/drawable-mdpi/ic_comment_white.png new file mode 100644 index 0000000000000000000000000000000000000000..a62f55cab1ab1dc32d1f0c2b915dd0f6114f6ab8 GIT binary patch literal 437 zcmV;m0ZRUfP)P000>X1^@s6#OZ}&00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP$`jBOZ=2) z*@^{lD{yN>A%qHt2DwFU==CwuId_H<;Kys^9yx_H-jS{L9j6#P;^2`Lz4}0&kTboD zS+yc$k6ArKMh2Z&=_|5H=whZ`Xb-ftrom1GJ;4f)*+?;cj@Uy6D3;Hz)r1{y5&Qx% zb1nu=G@2{1jndh~!==d)3P2_Q<2>)gJXggT%5bD{yJS-Tk;xUL$xP!0%1~)0oaQ~0 zfq&f7;_RwAHy|psNeR4J=ej|~SA`{-E=w@eh~sNblzn$y#2?h{5<+>i_Ni4-<{kZj z5A-}x+>UixKZ#j%p!bsou1l8ZU$DQ}J3%+=5bVzAX_V;AOco4V`0nu&@)P-ntDP5g fkwbC+|9AKT@3p}R{fgT<00000NkvXXu0mjf+E%t! literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_heart_white.png b/app/src/main/res/drawable-mdpi/ic_heart_white.png new file mode 100644 index 0000000000000000000000000000000000000000..86b4c250118130ed75cc6bb397b0010fd06e5100 GIT binary patch literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIXO=k$B+ufw^IUnn+*io`cs)^Fw5|8PHkvb zWMqH9Aj89chOwA|?}Uf}``w8#lcTR+65C|H?@7?1yI=QjyUrxTB5+eC$gA`z#rkgZ7zJ*y`eulPo!sVMgEP)}%;OXk;vd$@?2>@&$l?ng= literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/bg_home_recommend_channel.xml b/app/src/main/res/drawable/bg_home_recommend_channel.xml new file mode 100644 index 00000000..c5b4725e --- /dev/null +++ b/app/src/main/res/drawable/bg_home_recommend_channel.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/app/src/main/res/layout/item_home_recommend_channel.xml b/app/src/main/res/layout/item_home_recommend_channel.xml new file mode 100644 index 00000000..20c76b9b --- /dev/null +++ b/app/src/main/res/layout/item_home_recommend_channel.xml @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +