feat: 메인 홈

- 추천 채널 UI 추가
This commit is contained in:
2025-07-15 20:20:54 +09:00
parent 66a6f4bbab
commit db2e3bc8f2
7 changed files with 457 additions and 1 deletions

View File

@@ -36,7 +36,7 @@ android {
minSdk 23 minSdk 23
targetSdk 34 targetSdk 34
versionCode 174 versionCode 174
versionName "1.38.0" versionName "1.39.0"
} }
buildTypes { buildTypes {

View File

@@ -72,6 +72,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
private lateinit var auditionAdapter: AuditionBannerAdapter private lateinit var auditionAdapter: AuditionBannerAdapter
private lateinit var seriesDayOfWeekAdapter: HomeSeriesAdapter private lateinit var seriesDayOfWeekAdapter: HomeSeriesAdapter
private lateinit var weelyChartAdapter: HomeWeeklyChartAdapter private lateinit var weelyChartAdapter: HomeWeeklyChartAdapter
private lateinit var recommendChannelAdapter: HomeRecommendChannelAdapter
private lateinit var homeFreeContentAdapter: HomeContentAdapter private lateinit var homeFreeContentAdapter: HomeContentAdapter
private lateinit var curationAdapter: HomeCurationAdapter private lateinit var curationAdapter: HomeCurationAdapter
@@ -167,6 +168,7 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::infl
setupAudition() setupAudition()
setupSeriesDayOfWeek() setupSeriesDayOfWeek()
setupWeelyChart() setupWeelyChart()
setupRecommendChannel()
setupFreeContent() setupFreeContent()
setupCuration() setupCuration()
} }
@@ -860,6 +862,82 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(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() { private fun setupFreeContent() {
val spSectionTitle = SpannableString(binding.tvFreeContent.text) val spSectionTitle = SpannableString(binding.tvFreeContent.text)
spSectionTitle.setSpan( spSectionTitle.setSpan(

View File

@@ -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<HomeRecommendChannelAdapter.ViewHolder>() {
private var items = mutableListOf<RecommendChannelResponse>()
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<RecommendChannelResponse>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

View File

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

View File

@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:background="@drawable/bg_home_recommend_channel"
android:orientation="vertical"
android:padding="20dp">
<ImageView
android:id="@+id/iv_creator_profile"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/ll_creator_profile"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:gravity="center_vertical"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="@+id/iv_creator_profile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/iv_creator_profile"
app:layout_constraintTop_toTopOf="@+id/iv_creator_profile">
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/pretendard_bold"
android:textColor="@color/white"
android:textSize="24sp"
app:layout_constraintStart_toEndOf="@+id/iv_creator_profile"
app:layout_constraintTop_toTopOf="@+id/iv_creator_profile"
tools:text="닉네임" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/pretendard_regular"
android:text="콘텐츠"
android:textColor="@color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_content_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:fontFamily="@font/pretendard_bold"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="55" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_content_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_creator_profile">
<ImageView
android:id="@+id/iv_cover_1"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/pretendard_bold"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="고품격 음악밥솥" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_like_count_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_heart_white"
tools:text="999" />
<TextView
android:id="@+id/tv_comment_count_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_comment_white"
tools:text="999" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_content_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_content_1">
<ImageView
android:id="@+id/iv_cover_2"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/pretendard_bold"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="고품격 음악밥솥" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_like_count_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_heart_white"
tools:text="999" />
<TextView
android:id="@+id/tv_comment_count_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_comment_white"
tools:text="999" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_content_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_content_2">
<ImageView
android:id="@+id/iv_cover_3"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/pretendard_bold"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="고품격 음악밥솥" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_like_count_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_heart_white"
tools:text="999" />
<TextView
android:id="@+id/tv_comment_count_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:drawablePadding="4dp"
android:fontFamily="@font/pretendard_regular"
android:textColor="@color/white"
android:textSize="18sp"
app:drawableStartCompat="@drawable/ic_comment_white"
tools:text="999" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>