From f24cd97afa9261799b424a4b1574b2e075465ad4 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 15 Jul 2025 05:39:04 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-=20?= =?UTF-8?q?=EC=9D=B8=EA=B8=B0=20=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20UI=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/explorer/GetExplorerResponse.kt | 3 +- .../sodalive/home/CreatorRankingAdapter.kt | 86 ++++++++++++++++ .../vividnext/sodalive/home/HomeFragment.kt | 95 +++++++++++++++++- app/src/main/res/drawable-mdpi/rank1.png | Bin 0 -> 2394 bytes app/src/main/res/drawable-mdpi/rank2.png | Bin 0 -> 2114 bytes app/src/main/res/drawable-mdpi/rank3.png | Bin 0 -> 1954 bytes app/src/main/res/drawable/bg_home_creator.xml | 9 ++ app/src/main/res/layout/item_home_creator.xml | 58 +++++++++++ 8 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/home/CreatorRankingAdapter.kt create mode 100644 app/src/main/res/drawable-mdpi/rank1.png create mode 100644 app/src/main/res/drawable-mdpi/rank2.png create mode 100644 app/src/main/res/drawable-mdpi/rank3.png create mode 100644 app/src/main/res/drawable/bg_home_creator.xml create mode 100644 app/src/main/res/layout/item_home_creator.xml diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt index 9d9ce5a6..aedc1d6e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt @@ -22,5 +22,6 @@ data class GetExplorerSectionCreatorResponse( @SerializedName("id") val id: Long, @SerializedName("nickname") val nickname: String, @SerializedName("tags") val tags: String, - @SerializedName("profileImageUrl") val profileImageUrl: String + @SerializedName("profileImageUrl") val profileImageUrl: String, + @SerializedName("followerCount") val followerCount: Int ) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/CreatorRankingAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/CreatorRankingAdapter.kt new file mode 100644 index 00000000..a4e74611 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/CreatorRankingAdapter.kt @@ -0,0 +1,86 @@ +package kr.co.vividnext.sodalive.home + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CircleCrop +import com.bumptech.glide.request.RequestOptions +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemHomeCreatorBinding +import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse +import kr.co.vividnext.sodalive.extensions.moneyFormat + +class CreatorRankingAdapter( + private val onClickItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + private val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemHomeCreatorBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: GetExplorerSectionCreatorResponse, index: Int) { + binding.root.setOnClickListener { onClickItem(item.id) } + + Glide + .with(context) + .load(item.profileImageUrl) + .apply( + RequestOptions().transform( + CircleCrop() + ) + ) + .into(binding.ivProfile) + + binding.tvNickname.text = item.nickname + binding.tvFollower.text = item.followerCount.moneyFormat() + + when (index) { + 0 -> { + binding.ivCrown.setImageResource(R.drawable.rank1) + binding.ivCrown.visibility = View.VISIBLE + } + + 1 -> { + binding.ivCrown.setImageResource(R.drawable.rank2) + binding.ivCrown.visibility = View.VISIBLE + } + + 2 -> { + binding.ivCrown.setImageResource(R.drawable.rank3) + binding.ivCrown.visibility = View.VISIBLE + } + + else -> { + binding.ivCrown.visibility = View.GONE + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemHomeCreatorBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position], index = position) + } + + override fun getItemCount() = items.size + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } +} 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 e8204967..69886b91 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 @@ -26,6 +26,7 @@ import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.databinding.FragmentHomeBinding +import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.live.LiveViewModel import kr.co.vividnext.sodalive.live.room.LiveRoomActivity @@ -40,6 +41,7 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +@OptIn(UnstableApi::class) class HomeFragment : BaseFragment(FragmentHomeBinding::inflate) { private val viewModel: HomeViewModel by inject() private val liveViewModel: LiveViewModel by inject() @@ -47,6 +49,7 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl private lateinit var loadingDialog: LoadingDialog private lateinit var liveAdapter: HomeLiveAdapter + private lateinit var creatorRankingAdapter: CreatorRankingAdapter private val handler = Handler(Looper.getMainLooper()) @@ -133,9 +136,10 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl } setupLiveView() + setupSectionCreator() + setupLatestContent() } - @OptIn(UnstableApi::class) private fun setupLiveView() { val spSectionTitle = SpannableString(binding.tvLiveTitle.text) spSectionTitle.setSpan( @@ -191,17 +195,17 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl when (parent.getChildAdapterPosition(view)) { 0 -> { outRect.left = 0 - outRect.right = 16f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() } liveAdapter.itemCount - 1 -> { - outRect.left = 16f.dpToPx().toInt() + outRect.left = 8f.dpToPx().toInt() outRect.right = 0 } else -> { - outRect.left = 16f.dpToPx().toInt() - outRect.right = 16f.dpToPx().toInt() + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() } } } @@ -219,6 +223,87 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl } } + fun setupSectionCreator() { + val spSectionTitle = SpannableString(binding.tvFamousCreatorTitle.text) + spSectionTitle.setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + requireContext(), + R.color.color_3bb9f1 + ) + ), + 0, + 2, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + binding.tvFamousCreatorTitle.text = spSectionTitle + + creatorRankingAdapter = CreatorRankingAdapter { + if (SharedPreferenceManager.token.isNotBlank()) { + startActivity( + Intent( + requireActivity(), + UserProfileActivity::class.java + ).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + } else { + (requireActivity() as MainActivity).showLoginActivity() + } + } + + val recyclerView = binding.rvFamousCreator + 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() + } + + creatorRankingAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = creatorRankingAdapter + + viewModel.creatorRankingLiveData.observe(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.llFamousCreator.visibility = View.VISIBLE + creatorRankingAdapter.addItems(it) + } else { + binding.llFamousCreator.visibility = View.GONE + } + } + } + + private fun setupLatestContent() { + + } + private fun bindData() { viewModel.isLoading.observe(viewLifecycleOwner) { if (it) { diff --git a/app/src/main/res/drawable-mdpi/rank1.png b/app/src/main/res/drawable-mdpi/rank1.png new file mode 100644 index 0000000000000000000000000000000000000000..84be27e15a0824c87f16b6b7d121718e304ff341 GIT binary patch literal 2394 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/bg_home_creator.xml b/app/src/main/res/drawable/bg_home_creator.xml new file mode 100644 index 00000000..5ad41476 --- /dev/null +++ b/app/src/main/res/drawable/bg_home_creator.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/layout/item_home_creator.xml b/app/src/main/res/layout/item_home_creator.xml new file mode 100644 index 00000000..fc384b1a --- /dev/null +++ b/app/src/main/res/layout/item_home_creator.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + +