From 8bc1ec58301b2be23d1787e207cf1ce87777fd43 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 24 Jun 2026 13:35:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(content):=20=EB=9E=AD=ED=82=B9=20=EB=B3=80?= =?UTF-8?q?=EB=8F=99=20=EC=88=A8=EA=B9=80=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentRankingGridCardView.kt | 8 +- .../ContentRankingHorizontalCardView.kt | 19 +++- .../ContentRankingLargeCardView.kt | 8 +- .../ContentRankingCardViewTest.kt | 86 +++++++++++++++++++ 4 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 app/src/test/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingCardViewTest.kt diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingGridCardView.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingGridCardView.kt index 723d1632..413829c7 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingGridCardView.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingGridCardView.kt @@ -80,6 +80,9 @@ open class ContentRankingGridCardView @JvmOverloads constructor( } private fun bindDelta(item: ContentRankingItem) { + requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE + if (!item.showRankChange) return + val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount) applyDeltaContainer(presentation) requireNotNull(deltaIcon).apply { @@ -150,7 +153,10 @@ open class ContentRankingGridCardView @JvmOverloads constructor( } private fun placeLabel(width: Int, top: Int, scale: Float, size: ContentRankingCardSize) { - findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams((width * scale).roundToInt(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { + findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams( + (width * scale).roundToInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { leftMargin = ((size.widthPx - (width * scale)) / 2f).roundToInt() topMargin = (top * scale).roundToInt() } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingHorizontalCardView.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingHorizontalCardView.kt index 7be430ad..3c6dd3dc 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingHorizontalCardView.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingHorizontalCardView.kt @@ -79,6 +79,10 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor( } private fun bindDelta(item: ContentRankingItem) { + val deltaGroup = findViewById(R.id.ll_content_ranking_delta) + deltaGroup.visibility = if (item.showRankChange) View.VISIBLE else View.GONE + if (!item.showRankChange) return + val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount) applyDeltaContainer(presentation) requireNotNull(deltaIcon).apply { @@ -111,7 +115,10 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor( private fun positionViews(size: ContentRankingCardSize) { val scale = size.widthPx / 374f - requireNotNull(rankGroup).layoutParams = LayoutParams((49 * scale).roundToInt(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { + requireNotNull(rankGroup).layoutParams = LayoutParams( + (49 * scale).roundToInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { leftMargin = (14 * scale).roundToInt() topMargin = (14 * scale).roundToInt() } @@ -120,11 +127,17 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor( leftMargin = (77 * scale).roundToInt() topMargin = (10 * scale).roundToInt() } - findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams((189 * scale).roundToInt(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { + findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams( + (189 * scale).roundToInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { leftMargin = (171 * scale).roundToInt() topMargin = (31 * scale).roundToInt() } - requireNotNull(inaccessibleText).layoutParams = LayoutParams((189 * scale).roundToInt(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { + requireNotNull(inaccessibleText).layoutParams = LayoutParams( + (189 * scale).roundToInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { leftMargin = (171 * scale).roundToInt() topMargin = (38 * scale).roundToInt() } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingLargeCardView.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingLargeCardView.kt index 5f93e9fa..bbbac0a2 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingLargeCardView.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingLargeCardView.kt @@ -84,6 +84,9 @@ class ContentRankingLargeCardView @JvmOverloads constructor( } private fun bindDelta(item: ContentRankingItem) { + requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE + if (!item.showRankChange) return + val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount) applyDeltaContainer(presentation) requireNotNull(deltaIcon).apply { @@ -128,7 +131,10 @@ class ContentRankingLargeCardView @JvmOverloads constructor( leftMargin = (20 * scale).roundToInt() topMargin = (109 * scale).roundToInt() } - findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams((165 * scale).roundToInt(), ViewGroup.LayoutParams.WRAP_CONTENT).apply { + findViewById(R.id.ll_content_ranking_label).layoutParams = LayoutParams( + (165 * scale).roundToInt(), + ViewGroup.LayoutParams.WRAP_CONTENT + ).apply { leftMargin = ((size.widthPx - (165 * scale)) / 2f).roundToInt() topMargin = (182 * scale).roundToInt() } diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingCardViewTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingCardViewTest.kt new file mode 100644 index 00000000..08634a23 --- /dev/null +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/widget/contentranking/ContentRankingCardViewTest.kt @@ -0,0 +1,86 @@ +package kr.co.vividnext.sodalive.v2.widget.contentranking + +import android.app.Application +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import androidx.test.core.app.ApplicationProvider +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.v2.widget.ranking.RankingChangeType.Increase +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [28], application = Application::class) +class ContentRankingCardViewTest { + + @Test + fun `large card는 showRankChange가 false이면 rank num을 숨긴다`() { + val view = inflateView(R.layout.view_content_ranking_large_card) + + view.bind(sampleItem(showRankChange = false)) + + assertEquals(View.GONE, view.findViewById(R.id.ll_content_ranking_delta).visibility) + } + + @Test + fun `medium grid card는 showRankChange가 false이면 rank num을 숨긴다`() { + val view = inflateView(R.layout.view_content_ranking_medium_grid_card) + + view.bind(sampleItem(rank = 2, showRankChange = false)) + + assertEquals(View.GONE, view.findViewById(R.id.ll_content_ranking_delta).visibility) + } + + @Test + fun `small grid card는 showRankChange가 false이면 rank num을 숨긴다`() { + val view = inflateView(R.layout.view_content_ranking_small_grid_card) + + view.bind(sampleItem(rank = 8, showRankChange = false)) + + assertEquals(View.GONE, view.findViewById(R.id.ll_content_ranking_delta).visibility) + } + + @Test + fun `horizontal card는 showRankChange가 false이면 rank num을 숨긴다`() { + val view = inflateView(R.layout.view_content_ranking_horizontal_card) + + view.bind(sampleItem(rank = 11, showRankChange = false)) + + assertEquals(View.GONE, view.findViewById(R.id.ll_content_ranking_delta).visibility) + } + + @Test + fun `card는 showRankChange가 true이면 rank num을 보여준다`() { + val view = inflateView(R.layout.view_content_ranking_large_card) + + view.bind(sampleItem(showRankChange = true)) + + assertEquals(View.VISIBLE, view.findViewById(R.id.ll_content_ranking_delta).visibility) + } + + private inline fun inflateView(layoutResId: Int): T { + val context = ApplicationProvider.getApplicationContext() + return LayoutInflater.from(context).inflate(layoutResId, null, false) as T + } + + private fun sampleItem( + rank: Int = 1, + showRankChange: Boolean = true + ) = ContentRankingItem( + contentId = "content-1", + creatorId = "creator-1", + rank = rank, + previousRank = 5, + rankChangeType = Increase, + rankChangeAmount = 4, + contentName = "콘텐츠 이름", + creatorName = "크리에이터 이름", + imageUrl = "https://example.com/image.png", + isBlocked = false, + showRankChange = showRankChange + ) +}