feat: 메인 홈

- 인기 크리에이터 UI 추가
This commit is contained in:
2025-07-15 05:39:04 +09:00
parent 388770889f
commit f24cd97afa
8 changed files with 245 additions and 6 deletions

View File

@@ -22,5 +22,6 @@ data class GetExplorerSectionCreatorResponse(
@SerializedName("id") val id: Long, @SerializedName("id") val id: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("profileImageUrl") val profileImageUrl: String @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("followerCount") val followerCount: Int
) )

View File

@@ -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<CreatorRankingAdapter.ViewHolder>() {
private val items = mutableListOf<GetExplorerSectionCreatorResponse>()
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<GetExplorerSectionCreatorResponse>) {
this.items.addAll(items)
notifyDataSetChanged()
}
}

View File

@@ -26,6 +26,7 @@ import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentHomeBinding 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.extensions.dpToPx
import kr.co.vividnext.sodalive.live.LiveViewModel import kr.co.vividnext.sodalive.live.LiveViewModel
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
@@ -40,6 +41,7 @@ import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@OptIn(UnstableApi::class)
class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) { class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
private val viewModel: HomeViewModel by inject() private val viewModel: HomeViewModel by inject()
private val liveViewModel: LiveViewModel by inject() private val liveViewModel: LiveViewModel by inject()
@@ -47,6 +49,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var liveAdapter: HomeLiveAdapter private lateinit var liveAdapter: HomeLiveAdapter
private lateinit var creatorRankingAdapter: CreatorRankingAdapter
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@@ -133,9 +136,10 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
} }
setupLiveView() setupLiveView()
setupSectionCreator()
setupLatestContent()
} }
@OptIn(UnstableApi::class)
private fun setupLiveView() { private fun setupLiveView() {
val spSectionTitle = SpannableString(binding.tvLiveTitle.text) val spSectionTitle = SpannableString(binding.tvLiveTitle.text)
spSectionTitle.setSpan( spSectionTitle.setSpan(
@@ -191,17 +195,17 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
when (parent.getChildAdapterPosition(view)) { when (parent.getChildAdapterPosition(view)) {
0 -> { 0 -> {
outRect.left = 0 outRect.left = 0
outRect.right = 16f.dpToPx().toInt() outRect.right = 8f.dpToPx().toInt()
} }
liveAdapter.itemCount - 1 -> { liveAdapter.itemCount - 1 -> {
outRect.left = 16f.dpToPx().toInt() outRect.left = 8f.dpToPx().toInt()
outRect.right = 0 outRect.right = 0
} }
else -> { else -> {
outRect.left = 16f.dpToPx().toInt() outRect.left = 8f.dpToPx().toInt()
outRect.right = 16f.dpToPx().toInt() outRect.right = 8f.dpToPx().toInt()
} }
} }
} }
@@ -219,6 +223,87 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(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() { private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) { viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) { if (it) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="16dp" />
<gradient
android:angle="-45"
android:endColor="#2A339D"
android:startColor="#5ACDE1"
android:type="linear" />
</shape>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="133dp"
android:layout_height="188dp"
android:layout_marginTop="20dp"
android:background="@drawable/bg_home_creator"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_profile"
android:layout_width="70dp"
android:layout_height="70dp"
android:contentDescription="@null" />
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="도화" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/pretendard_regular"
android:text="팔로워"
android:textColor="#78909C"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_follower"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="#78909C"
android:textSize="14sp"
tools:text="111" />
</LinearLayout>
<ImageView
android:id="@+id/iv_crown"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:contentDescription="@null"
tools:src="@drawable/rank1" />
</RelativeLayout>