feat(widget): 오디오 콘텐츠 카드 컴포넌트를 추가한다

This commit is contained in:
2026-05-19 23:52:53 +09:00
parent 30264935dc
commit 6fda122091
7 changed files with 704 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
package kr.co.vividnext.sodalive.v2.widget
import androidx.annotation.StyleRes
import kr.co.vividnext.sodalive.R
sealed class AudioContentCardSize(
val cardWidthDp: Int,
val thumbnailSizeDp: Int,
val labelWidthDp: Int,
val thumbnailLabelGapDp: Int,
@get:StyleRes val titleStyleRes: Int,
@get:StyleRes val creatorStyleRes: Int
) {
data object Large : AudioContentCardSize(
cardWidthDp = 185,
thumbnailSizeDp = 185,
labelWidthDp = 185,
thumbnailLabelGapDp = 11,
titleStyleRes = R.style.Typography_Heading4,
creatorStyleRes = R.style.Typography_Body5
)
data object Medium : AudioContentCardSize(
cardWidthDp = 163,
thumbnailSizeDp = 163,
labelWidthDp = 151,
thumbnailLabelGapDp = 8,
titleStyleRes = R.style.Typography_Heading4,
creatorStyleRes = R.style.Typography_Body5
)
data object Small : AudioContentCardSize(
cardWidthDp = 122,
thumbnailSizeDp = 122,
labelWidthDp = 114,
thumbnailLabelGapDp = 8,
titleStyleRes = R.style.Typography_Body1,
creatorStyleRes = R.style.Typography_Caption2
)
}

View File

@@ -0,0 +1,90 @@
package kr.co.vividnext.sodalive.v2.widget
import android.content.Context
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import kr.co.vividnext.sodalive.R
import kotlin.math.roundToInt
class AudioContentCardView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private var thumbnail: ImageView? = null
private var labelContainer: LinearLayout? = null
private var titleText: TextView? = null
private var creatorText: TextView? = null
init {
orientation = VERTICAL
}
override fun onFinishInflate() {
super.onFinishInflate()
thumbnail = findViewById(R.id.iv_audio_content_thumbnail)
labelContainer = findViewById(R.id.ll_audio_content_label)
titleText = findViewById(R.id.tv_audio_content_title)
creatorText = findViewById(R.id.tv_audio_content_creator)
setSize(AudioContentCardSize.Medium)
}
fun setSize(size: AudioContentCardSize) {
updateRootWidth(size.cardWidthDp.dpToPx())
requireNotNull(thumbnail).layoutParams = LayoutParams(
size.thumbnailSizeDp.dpToPx(),
size.thumbnailSizeDp.dpToPx()
)
requireNotNull(labelContainer).layoutParams = LayoutParams(
size.labelWidthDp.dpToPx(),
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
topMargin = size.thumbnailLabelGapDp.dpToPx()
}
requireNotNull(titleText).setTextAppearance(size.titleStyleRes)
requireNotNull(creatorText).apply {
setTextAppearance(size.creatorStyleRes)
layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
topMargin = TITLE_CREATOR_GAP_DP.dpToPx()
}
}
}
fun setContent(
title: String,
creatorName: String
) {
requireNotNull(titleText).text = title
requireNotNull(creatorText).text = creatorName
}
fun thumbnailView(): ImageView = requireNotNull(thumbnail)
private fun updateRootWidth(width: Int) {
val currentLayoutParams = layoutParams
layoutParams = if (currentLayoutParams == null) {
ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT)
} else {
currentLayoutParams.apply {
this.width = width
this.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
}
}
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).roundToInt()
private companion object {
const val TITLE_CREATOR_GAP_DP = 2
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/gray_900" />
<corners android:radius="@dimen/radius_14" />
</shape>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<kr.co.vividnext.sodalive.v2.widget.AudioContentCardView 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"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_audio_content_thumbnail"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_audio_content_card_thumbnail"
android:contentDescription="@null"
android:scaleType="centerCrop" />
<LinearLayout
android:id="@+id/ll_audio_content_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_audio_content_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
tools:text="콘텐츠 제목" />
<TextView
android:id="@+id/tv_audio_content_creator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/gray_500"
tools:text="크리에이터 이름" />
</LinearLayout>
</kr.co.vividnext.sodalive.v2.widget.AudioContentCardView>