fix(feed): 커뮤니티 이미지 원본비율 높이를 적용한다
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.widget.feed
|
||||||
|
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
fun calculateFeedCommunityImageHeight(
|
||||||
|
containerWidthPx: Int,
|
||||||
|
intrinsicWidthPx: Int,
|
||||||
|
intrinsicHeightPx: Int
|
||||||
|
): Int? {
|
||||||
|
if (containerWidthPx <= 0 || intrinsicWidthPx <= 0 || intrinsicHeightPx <= 0) return null
|
||||||
|
return (containerWidthPx * intrinsicHeightPx.toFloat() / intrinsicWidthPx).roundToInt()
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@ class FeedCommunityView @JvmOverloads constructor(
|
|||||||
val isLocked = item.price > 0 && !item.existOrdered
|
val isLocked = item.price > 0 && !item.existOrdered
|
||||||
val hasImage = !item.imageUrl.isNullOrBlank()
|
val hasImage = !item.imageUrl.isNullOrBlank()
|
||||||
requireNotNull(communityImageContainer).isVisible = hasImage || isLocked
|
requireNotNull(communityImageContainer).isVisible = hasImage || isLocked
|
||||||
|
resetCommunityImageHeight()
|
||||||
requireNotNull(communityImage).isVisible = hasImage
|
requireNotNull(communityImage).isVisible = hasImage
|
||||||
if (!hasImage || isLocked) {
|
if (!hasImage || isLocked) {
|
||||||
requireNotNull(communityImage).setImageDrawable(null)
|
requireNotNull(communityImage).setImageDrawable(null)
|
||||||
@@ -79,6 +80,33 @@ class FeedCommunityView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun boundItem(): FeedItem.Community? = currentItem
|
fun boundItem(): FeedItem.Community? = currentItem
|
||||||
|
|
||||||
|
fun applyCommunityImageSize(
|
||||||
|
intrinsicWidthPx: Int,
|
||||||
|
intrinsicHeightPx: Int
|
||||||
|
) {
|
||||||
|
val container = requireNotNull(communityImageContainer)
|
||||||
|
val width = container.width.takeIf { it > 0 }
|
||||||
|
?: container.measuredWidth.takeIf { it > 0 }
|
||||||
|
?: (width - paddingLeft - paddingRight).takeIf { it > 0 }
|
||||||
|
?: run {
|
||||||
|
post { applyCommunityImageSize(intrinsicWidthPx, intrinsicHeightPx) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val imageHeight = calculateFeedCommunityImageHeight(
|
||||||
|
containerWidthPx = width,
|
||||||
|
intrinsicWidthPx = intrinsicWidthPx,
|
||||||
|
intrinsicHeightPx = intrinsicHeightPx
|
||||||
|
) ?: return
|
||||||
|
container.layoutParams = container.layoutParams.apply { height = imageHeight }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetCommunityImageHeight() {
|
||||||
|
val container = requireNotNull(communityImageContainer)
|
||||||
|
container.layoutParams = container.layoutParams.apply {
|
||||||
|
height = DEFAULT_IMAGE_HEIGHT_DP.dpToPx()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setFeedSize(size: FeedSize) {
|
fun setFeedSize(size: FeedSize) {
|
||||||
updateRootWidth(size.rootWidthDp.dpToPx())
|
updateRootWidth(size.rootWidthDp.dpToPx())
|
||||||
}
|
}
|
||||||
@@ -128,5 +156,6 @@ class FeedCommunityView @JvmOverloads constructor(
|
|||||||
private companion object {
|
private companion object {
|
||||||
const val CARD_RADIUS_DP = 14
|
const val CARD_RADIUS_DP = 14
|
||||||
const val COMMUNITY_IMAGE_RADIUS_DP = 14
|
const val COMMUNITY_IMAGE_RADIUS_DP = 14
|
||||||
|
const val DEFAULT_IMAGE_HEIGHT_DP = 236
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,9 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/fl_feed_community_image_container"
|
android:id="@+id/fl_feed_community_image_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="236dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/spacing_14"
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:background="@drawable/bg_feed_community_image"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible">
|
tools:visibility="visible">
|
||||||
|
|
||||||
@@ -76,9 +77,8 @@
|
|||||||
android:id="@+id/iv_feed_community_image"
|
android:id="@+id/iv_feed_community_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/bg_feed_community_image"
|
|
||||||
android:contentDescription="@string/a11y_feed_content_image"
|
android:contentDescription="@string/a11y_feed_content_image"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="fitCenter"
|
||||||
tools:src="@drawable/ic_launcher_background" />
|
tools:src="@drawable/ic_launcher_background" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.widget.feed
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class FeedCommunityImageSizeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `커뮤니티 이미지는 원본 비율로 높이를 계산한다`() {
|
||||||
|
val height = calculateFeedCommunityImageHeight(
|
||||||
|
containerWidthPx = 300,
|
||||||
|
intrinsicWidthPx = 1200,
|
||||||
|
intrinsicHeightPx = 800
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(200, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `커뮤니티 이미지는 특정 최대 높이로 제한하지 않는다`() {
|
||||||
|
val height = calculateFeedCommunityImageHeight(
|
||||||
|
containerWidthPx = 300,
|
||||||
|
intrinsicWidthPx = 600,
|
||||||
|
intrinsicHeightPx = 1800
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(900, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `커뮤니티 이미지는 크기 정보가 없으면 높이를 계산하지 않는다`() {
|
||||||
|
val height = calculateFeedCommunityImageHeight(
|
||||||
|
containerWidthPx = 300,
|
||||||
|
intrinsicWidthPx = 0,
|
||||||
|
intrinsicHeightPx = 800
|
||||||
|
)
|
||||||
|
|
||||||
|
assertNull(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,14 +85,15 @@ class FeedViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `community layout matches creator channel feed row structure`() {
|
fun `community layout uses runtime image height instead of fixed crop height`() {
|
||||||
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
|
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
|
||||||
val layout = projectFile("app/src/main/res/layout/view_feed_community.xml").readText()
|
val layout = projectFile("app/src/main/res/layout/view_feed_community.xml").readText()
|
||||||
|
|
||||||
assertFalse(layout.contains("@+id/tv_feed_community_keyword"))
|
assertFalse(layout.contains("@+id/tv_feed_community_keyword"))
|
||||||
assertTrue(layout.contains("android:layout_width=\"match_parent\""))
|
assertTrue(layout.contains("android:layout_width=\"match_parent\""))
|
||||||
assertTrue(layout.contains("android:maxLines=\"5\""))
|
assertTrue(layout.contains("android:maxLines=\"5\""))
|
||||||
assertTrue(layout.contains("android:layout_height=\"236dp\""))
|
assertFalse(layout.contains("android:layout_height=\"236dp\""))
|
||||||
|
assertTrue(layout.contains("android:scaleType=\"fitCenter\""))
|
||||||
assertTrue(layout.contains("@drawable/bg_creator_channel_community_price"))
|
assertTrue(layout.contains("@drawable/bg_creator_channel_community_price"))
|
||||||
assertTrue(layout.contains("@drawable/ic_bar_cash"))
|
assertTrue(layout.contains("@drawable/ic_bar_cash"))
|
||||||
assertFalse(layout.contains("android:drawableStart=\"@drawable/ic_bar_cash\""))
|
assertFalse(layout.contains("android:drawableStart=\"@drawable/ic_bar_cash\""))
|
||||||
@@ -152,17 +153,21 @@ class FeedViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `community image container keeps creator channel fixed height`() {
|
fun `community image container keeps fallback height until original image ratio is applied`() {
|
||||||
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
|
val view = inflateView<FeedCommunityView>(R.layout.view_feed_community)
|
||||||
val imageContainer = view.findViewById<FrameLayout>(R.id.fl_feed_community_image_container)
|
val imageContainer = view.findViewById<FrameLayout>(R.id.fl_feed_community_image_container)
|
||||||
|
|
||||||
view.bind(sampleCommunityItem(bodyText = "본문", keywordText = "", imageUrl = "https://example.com/post.png"))
|
view.bind(sampleCommunityItem(bodyText = "본문", keywordText = "", imageUrl = "https://example.com/post.png"))
|
||||||
|
view.measure(exactly(402.dpToPx()), View.MeasureSpec.UNSPECIFIED)
|
||||||
|
|
||||||
|
assertEquals(236.dpToPx(), imageContainer.measuredHeight)
|
||||||
|
|
||||||
|
view.applyCommunityImageSize(intrinsicWidthPx = 600, intrinsicHeightPx = 1800)
|
||||||
view.measure(exactly(402.dpToPx()), View.MeasureSpec.UNSPECIFIED)
|
view.measure(exactly(402.dpToPx()), View.MeasureSpec.UNSPECIFIED)
|
||||||
|
|
||||||
val expectedImageWidth = 402.dpToPx() - view.paddingLeft - view.paddingRight
|
val expectedImageWidth = 402.dpToPx() - view.paddingLeft - view.paddingRight
|
||||||
assertEquals(expectedImageWidth, imageContainer.measuredWidth)
|
assertEquals(expectedImageWidth, imageContainer.measuredWidth)
|
||||||
assertEquals(236.dpToPx(), imageContainer.measuredHeight)
|
assertEquals(expectedImageWidth * 3, imageContainer.measuredHeight)
|
||||||
assertEquals(true, imageContainer.clipToOutline)
|
assertEquals(true, imageContainer.clipToOutline)
|
||||||
assertNotNull(imageContainer.outlineProvider)
|
assertNotNull(imageContainer.outlineProvider)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user