fix(content-ranking): 카드 UI 크기를 반응형 적용한다
This commit is contained in:
@@ -6,6 +6,7 @@ import android.graphics.RenderEffect
|
|||||||
import android.graphics.Shader
|
import android.graphics.Shader
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
@@ -13,6 +14,7 @@ import android.widget.FrameLayout
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingResponsiveScale
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
open class ContentRankingGridCardView @JvmOverloads constructor(
|
open class ContentRankingGridCardView @JvmOverloads constructor(
|
||||||
@@ -29,6 +31,7 @@ open class ContentRankingGridCardView @JvmOverloads constructor(
|
|||||||
private var titleText: TextView? = null
|
private var titleText: TextView? = null
|
||||||
private var creatorText: TextView? = null
|
private var creatorText: TextView? = null
|
||||||
private var currentItem: ContentRankingItem? = null
|
private var currentItem: ContentRankingItem? = null
|
||||||
|
private var currentTextStyle: ContentRankingTextStyle? = null
|
||||||
private var clickListener: ((ContentRankingItem) -> Unit)? = null
|
private var clickListener: ((ContentRankingItem) -> Unit)? = null
|
||||||
|
|
||||||
override fun onFinishInflate() {
|
override fun onFinishInflate() {
|
||||||
@@ -48,11 +51,13 @@ open class ContentRankingGridCardView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun bind(item: ContentRankingItem) {
|
fun bind(item: ContentRankingItem) {
|
||||||
currentItem = item
|
currentItem = item
|
||||||
|
val textStyle = currentTextStyle ?: defaultTextStyle()
|
||||||
|
applyTextStyle(textStyle)
|
||||||
requireNotNull(rankText).apply {
|
requireNotNull(rankText).apply {
|
||||||
text = item.rank.toString()
|
text = item.rank.toString()
|
||||||
applyContentRankingRankGradient()
|
applyContentRankingRankGradient()
|
||||||
}
|
}
|
||||||
bindDelta(item)
|
bindDelta(item, textStyle)
|
||||||
requireNotNull(titleText).apply {
|
requireNotNull(titleText).apply {
|
||||||
text = item.displayContentName(context.getString(R.string.content_ranking_inaccessible_info))
|
text = item.displayContentName(context.getString(R.string.content_ranking_inaccessible_info))
|
||||||
visibility = if (text.isNullOrBlank()) View.INVISIBLE else View.VISIBLE
|
visibility = if (text.isNullOrBlank()) View.INVISIBLE else View.VISIBLE
|
||||||
@@ -79,34 +84,58 @@ open class ContentRankingGridCardView @JvmOverloads constructor(
|
|||||||
currentItem?.let(::applyAccessState)
|
currentItem?.let(::applyAccessState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindDelta(item: ContentRankingItem) {
|
private fun bindDelta(
|
||||||
|
item: ContentRankingItem,
|
||||||
|
textStyle: ContentRankingTextStyle
|
||||||
|
) {
|
||||||
requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
||||||
if (!item.showRankChange) return
|
if (!item.showRankChange) return
|
||||||
|
|
||||||
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
||||||
applyDeltaContainer(presentation)
|
val scale = RankingResponsiveScale.fromResources(resources)
|
||||||
|
applyDeltaContainer(presentation, scale)
|
||||||
requireNotNull(deltaIcon).apply {
|
requireNotNull(deltaIcon).apply {
|
||||||
setImageResource(presentation.iconRes)
|
setImageResource(presentation.iconRes)
|
||||||
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||||
width = presentation.iconWidthDp.dpToPx()
|
width = presentation.iconWidthDp.dpToPx(scale)
|
||||||
height = presentation.iconHeightDp.dpToPx()
|
height = presentation.iconHeightDp.dpToPx(scale)
|
||||||
marginStart = presentation.iconMarginStartDp.dpToPx()
|
marginStart = presentation.iconMarginStartDp.dpToPx(scale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requireNotNull(deltaAmountText).apply {
|
requireNotNull(deltaAmountText).apply {
|
||||||
text = presentation.amountText
|
text = presentation.amountText
|
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.deltaTextSizeSp)
|
||||||
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyDeltaContainer(presentation: ContentRankingDeltaPresentation) {
|
private fun applyDeltaContainer(
|
||||||
|
presentation: ContentRankingDeltaPresentation,
|
||||||
|
scale: Float
|
||||||
|
) {
|
||||||
requireNotNull(deltaGroup).apply {
|
requireNotNull(deltaGroup).apply {
|
||||||
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
||||||
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx() else 0
|
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx(scale) else 0
|
||||||
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyTextStyle(style: ContentRankingTextStyle) {
|
||||||
|
requireNotNull(rankText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.rankTextSizeSp)
|
||||||
|
requireNotNull(titleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.titleTextSizeSp)
|
||||||
|
requireNotNull(creatorText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.creatorTextSizeSp)
|
||||||
|
requireNotNull(deltaAmountText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.deltaTextSizeSp)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultTextStyle(): ContentRankingTextStyle {
|
||||||
|
val scale = RankingResponsiveScale.fromResources(resources)
|
||||||
|
return if (this is ContentRankingSmallGridCardView) {
|
||||||
|
ContentRankingTextStyle.smallGrid(scale)
|
||||||
|
} else {
|
||||||
|
ContentRankingTextStyle.mediumGrid(scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyAccessState(item: ContentRankingItem) {
|
private fun applyAccessState(item: ContentRankingItem) {
|
||||||
applyBlur(item.isInaccessible)
|
applyBlur(item.isInaccessible)
|
||||||
isClickable = item.isTouchable && clickListener != null
|
isClickable = item.isTouchable && clickListener != null
|
||||||
@@ -120,26 +149,32 @@ open class ContentRankingGridCardView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private fun positionMedium(size: ContentRankingCardSize) {
|
private fun positionMedium(size: ContentRankingCardSize) {
|
||||||
val scale = size.widthPx / 185f
|
val scale = size.widthPx / 185f
|
||||||
|
val textStyle = ContentRankingTextStyle.mediumGrid(RankingResponsiveScale.fromResources(resources))
|
||||||
|
currentTextStyle = textStyle
|
||||||
requireNotNull(rankText).apply {
|
requireNotNull(rankText).apply {
|
||||||
textSize = 54f
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.rankTextSizeSp)
|
||||||
layoutParams = LayoutParams((55 * scale).roundToInt(), (75 * scale).roundToInt())
|
layoutParams = LayoutParams((55 * scale).roundToInt(), (75 * scale).roundToInt())
|
||||||
setPadding(0, 0, 0, (6 * scale).roundToInt())
|
setPadding(0, 0, 0, (6 * scale).roundToInt())
|
||||||
}
|
}
|
||||||
requireNotNull(titleText).textSize = 22f
|
requireNotNull(titleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.titleTextSizeSp)
|
||||||
|
requireNotNull(creatorText).setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.creatorTextSizeSp)
|
||||||
placeDelta(left = 10, top = 70, scale = scale)
|
placeDelta(left = 10, top = 70, scale = scale)
|
||||||
placeLabel(width = 165, top = 136, scale = scale, size = size)
|
placeLabel(width = 165, top = 136, scale = scale, size = size)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun positionSmall(size: ContentRankingCardSize) {
|
private fun positionSmall(size: ContentRankingCardSize) {
|
||||||
val scale = size.widthPx / 122f
|
val scale = size.widthPx / 122f
|
||||||
|
val textStyle = ContentRankingTextStyle.smallGrid(RankingResponsiveScale.fromResources(resources))
|
||||||
|
currentTextStyle = textStyle
|
||||||
requireNotNull(rankText).apply {
|
requireNotNull(rankText).apply {
|
||||||
textSize = 40f
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.rankTextSizeSp)
|
||||||
layoutParams = LayoutParams((42 * scale).roundToInt(), (56 * scale).roundToInt()).apply {
|
layoutParams = LayoutParams((42 * scale).roundToInt(), (56 * scale).roundToInt()).apply {
|
||||||
leftMargin = (8 * scale).roundToInt()
|
leftMargin = (8 * scale).roundToInt()
|
||||||
}
|
}
|
||||||
setPadding(0, 0, 0, (5 * scale).roundToInt())
|
setPadding(0, 0, 0, (5 * scale).roundToInt())
|
||||||
}
|
}
|
||||||
requireNotNull(titleText).textSize = 14f
|
requireNotNull(titleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.titleTextSizeSp)
|
||||||
|
requireNotNull(creatorText).setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.creatorTextSizeSp)
|
||||||
placeDelta(left = 10, top = 49, scale = scale)
|
placeDelta(left = 10, top = 49, scale = scale)
|
||||||
placeLabel(width = 102, top = 81, scale = scale, size = size)
|
placeLabel(width = 102, top = 81, scale = scale, size = size)
|
||||||
}
|
}
|
||||||
@@ -178,7 +213,7 @@ open class ContentRankingGridCardView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).roundToInt()
|
private fun Int.dpToPx(scale: Float = 1f): Int = (this * scale * resources.displayMetrics.density).roundToInt()
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val SMALL_THRESHOLD_PX = 140
|
const val SMALL_THRESHOLD_PX = 140
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.graphics.RenderEffect
|
|||||||
import android.graphics.Shader
|
import android.graphics.Shader
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
@@ -13,6 +14,7 @@ import android.widget.FrameLayout
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingResponsiveScale
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
||||||
@@ -48,11 +50,13 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun bind(item: ContentRankingItem) {
|
fun bind(item: ContentRankingItem) {
|
||||||
currentItem = item
|
currentItem = item
|
||||||
|
val textStyle = ContentRankingTextStyle.horizontal(RankingResponsiveScale.fromResources(resources))
|
||||||
|
applyTextStyle(textStyle)
|
||||||
requireNotNull(rankText).apply {
|
requireNotNull(rankText).apply {
|
||||||
text = item.rank.toString()
|
text = item.rank.toString()
|
||||||
applyContentRankingRankGradient()
|
applyContentRankingRankGradient()
|
||||||
}
|
}
|
||||||
bindDelta(item)
|
bindDelta(item, textStyle)
|
||||||
val inaccessibleMessage = context.getString(R.string.content_ranking_inaccessible_info)
|
val inaccessibleMessage = context.getString(R.string.content_ranking_inaccessible_info)
|
||||||
requireNotNull(titleText).text = item.displayContentName(inaccessibleMessage)
|
requireNotNull(titleText).text = item.displayContentName(inaccessibleMessage)
|
||||||
requireNotNull(creatorText).text = item.displayCreatorName()
|
requireNotNull(creatorText).text = item.displayCreatorName()
|
||||||
@@ -69,6 +73,7 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
|||||||
height = size.heightPx
|
height = size.heightPx
|
||||||
}
|
}
|
||||||
positionViews(size)
|
positionViews(size)
|
||||||
|
applyTextStyle(ContentRankingTextStyle.horizontal(RankingResponsiveScale.fromResources(resources)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun imageView(): ImageView = requireNotNull(image)
|
fun imageView(): ImageView = requireNotNull(image)
|
||||||
@@ -78,35 +83,51 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
|||||||
currentItem?.let(::applyAccessState)
|
currentItem?.let(::applyAccessState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindDelta(item: ContentRankingItem) {
|
private fun bindDelta(
|
||||||
|
item: ContentRankingItem,
|
||||||
|
textStyle: ContentRankingTextStyle
|
||||||
|
) {
|
||||||
val deltaGroup = findViewById<View>(R.id.ll_content_ranking_delta)
|
val deltaGroup = findViewById<View>(R.id.ll_content_ranking_delta)
|
||||||
deltaGroup.visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
deltaGroup.visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
||||||
if (!item.showRankChange) return
|
if (!item.showRankChange) return
|
||||||
|
|
||||||
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
||||||
applyDeltaContainer(presentation)
|
val scale = RankingResponsiveScale.fromResources(resources)
|
||||||
|
applyDeltaContainer(presentation, scale)
|
||||||
requireNotNull(deltaIcon).apply {
|
requireNotNull(deltaIcon).apply {
|
||||||
setImageResource(presentation.iconRes)
|
setImageResource(presentation.iconRes)
|
||||||
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||||
width = presentation.iconWidthDp.dpToPx()
|
width = presentation.iconWidthDp.dpToPx(scale)
|
||||||
height = presentation.iconHeightDp.dpToPx()
|
height = presentation.iconHeightDp.dpToPx(scale)
|
||||||
marginStart = presentation.iconMarginStartDp.dpToPx()
|
marginStart = presentation.iconMarginStartDp.dpToPx(scale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requireNotNull(deltaAmountText).apply {
|
requireNotNull(deltaAmountText).apply {
|
||||||
text = presentation.amountText
|
text = presentation.amountText
|
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.deltaTextSizeSp)
|
||||||
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyDeltaContainer(presentation: ContentRankingDeltaPresentation) {
|
private fun applyDeltaContainer(
|
||||||
|
presentation: ContentRankingDeltaPresentation,
|
||||||
|
scale: Float
|
||||||
|
) {
|
||||||
findViewById<View>(R.id.ll_content_ranking_delta).apply {
|
findViewById<View>(R.id.ll_content_ranking_delta).apply {
|
||||||
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
||||||
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx() else 0
|
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx(scale) else 0
|
||||||
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyTextStyle(style: ContentRankingTextStyle) {
|
||||||
|
requireNotNull(rankText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.rankTextSizeSp)
|
||||||
|
requireNotNull(titleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.titleTextSizeSp)
|
||||||
|
requireNotNull(creatorText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.creatorTextSizeSp)
|
||||||
|
requireNotNull(inaccessibleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.titleTextSizeSp)
|
||||||
|
requireNotNull(deltaAmountText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.deltaTextSizeSp)
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyAccessState(item: ContentRankingItem) {
|
private fun applyAccessState(item: ContentRankingItem) {
|
||||||
applyBlur(item.isInaccessible)
|
applyBlur(item.isInaccessible)
|
||||||
isClickable = item.isTouchable && clickListener != null
|
isClickable = item.isTouchable && clickListener != null
|
||||||
@@ -157,5 +178,5 @@ class ContentRankingHorizontalCardView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).roundToInt()
|
private fun Int.dpToPx(scale: Float = 1f): Int = (this * scale * resources.displayMetrics.density).roundToInt()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.graphics.RenderEffect
|
|||||||
import android.graphics.Shader
|
import android.graphics.Shader
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
@@ -13,6 +14,7 @@ import android.widget.FrameLayout
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.v2.widget.ranking.RankingResponsiveScale
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class ContentRankingLargeCardView @JvmOverloads constructor(
|
class ContentRankingLargeCardView @JvmOverloads constructor(
|
||||||
@@ -50,11 +52,13 @@ class ContentRankingLargeCardView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun bind(item: ContentRankingItem) {
|
fun bind(item: ContentRankingItem) {
|
||||||
currentItem = item
|
currentItem = item
|
||||||
|
val textStyle = ContentRankingTextStyle.large(RankingResponsiveScale.fromResources(resources))
|
||||||
|
applyTextStyle(textStyle)
|
||||||
requireNotNull(rankText).apply {
|
requireNotNull(rankText).apply {
|
||||||
text = item.rank.toString()
|
text = item.rank.toString()
|
||||||
applyContentRankingRankGradient()
|
applyContentRankingRankGradient()
|
||||||
}
|
}
|
||||||
bindDelta(item)
|
bindDelta(item, textStyle)
|
||||||
requireNotNull(titleText).apply {
|
requireNotNull(titleText).apply {
|
||||||
text = item.displayContentName(context.getString(R.string.content_ranking_inaccessible_info))
|
text = item.displayContentName(context.getString(R.string.content_ranking_inaccessible_info))
|
||||||
visibility = if (text.isNullOrBlank()) View.INVISIBLE else View.VISIBLE
|
visibility = if (text.isNullOrBlank()) View.INVISIBLE else View.VISIBLE
|
||||||
@@ -72,6 +76,7 @@ class ContentRankingLargeCardView @JvmOverloads constructor(
|
|||||||
height = size.heightPx
|
height = size.heightPx
|
||||||
}
|
}
|
||||||
positionViews(size)
|
positionViews(size)
|
||||||
|
applyTextStyle(ContentRankingTextStyle.large(RankingResponsiveScale.fromResources(resources)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun imageView(): ImageView = contentImageView()
|
fun imageView(): ImageView = contentImageView()
|
||||||
@@ -83,34 +88,49 @@ class ContentRankingLargeCardView @JvmOverloads constructor(
|
|||||||
currentItem?.let(::applyAccessState)
|
currentItem?.let(::applyAccessState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindDelta(item: ContentRankingItem) {
|
private fun bindDelta(
|
||||||
|
item: ContentRankingItem,
|
||||||
|
textStyle: ContentRankingTextStyle
|
||||||
|
) {
|
||||||
requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
requireNotNull(deltaGroup).visibility = if (item.showRankChange) View.VISIBLE else View.GONE
|
||||||
if (!item.showRankChange) return
|
if (!item.showRankChange) return
|
||||||
|
|
||||||
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
val presentation = ContentRankingDeltaPresentation.from(item.rankChangeType, item.rankChangeAmount)
|
||||||
applyDeltaContainer(presentation)
|
val scale = RankingResponsiveScale.fromResources(resources)
|
||||||
|
applyDeltaContainer(presentation, scale)
|
||||||
requireNotNull(deltaIcon).apply {
|
requireNotNull(deltaIcon).apply {
|
||||||
setImageResource(presentation.iconRes)
|
setImageResource(presentation.iconRes)
|
||||||
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
layoutParams = (layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||||
width = presentation.iconWidthDp.dpToPx()
|
width = presentation.iconWidthDp.dpToPx(scale)
|
||||||
height = presentation.iconHeightDp.dpToPx()
|
height = presentation.iconHeightDp.dpToPx(scale)
|
||||||
marginStart = presentation.iconMarginStartDp.dpToPx()
|
marginStart = presentation.iconMarginStartDp.dpToPx(scale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requireNotNull(deltaAmountText).apply {
|
requireNotNull(deltaAmountText).apply {
|
||||||
text = presentation.amountText
|
text = presentation.amountText
|
||||||
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, textStyle.deltaTextSizeSp)
|
||||||
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
visibility = if (presentation.showAmount) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyDeltaContainer(presentation: ContentRankingDeltaPresentation) {
|
private fun applyDeltaContainer(
|
||||||
|
presentation: ContentRankingDeltaPresentation,
|
||||||
|
scale: Float
|
||||||
|
) {
|
||||||
requireNotNull(deltaGroup).apply {
|
requireNotNull(deltaGroup).apply {
|
||||||
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
setBackgroundResource(if (presentation.showPillBackground) R.drawable.bg_creator_ranking_delta else 0)
|
||||||
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx() else 0
|
val horizontalPadding = if (presentation.showPillBackground) 4.dpToPx(scale) else 0
|
||||||
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
setPadding(horizontalPadding, paddingTop, horizontalPadding, paddingBottom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyTextStyle(style: ContentRankingTextStyle) {
|
||||||
|
requireNotNull(rankText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.rankTextSizeSp)
|
||||||
|
requireNotNull(titleText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.titleTextSizeSp)
|
||||||
|
requireNotNull(creatorText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.creatorTextSizeSp)
|
||||||
|
requireNotNull(deltaAmountText).setTextSize(TypedValue.COMPLEX_UNIT_SP, style.deltaTextSizeSp)
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyAccessState(item: ContentRankingItem) {
|
private fun applyAccessState(item: ContentRankingItem) {
|
||||||
applyBlur(item.isInaccessible)
|
applyBlur(item.isInaccessible)
|
||||||
isClickable = item.isTouchable && clickListener != null
|
isClickable = item.isTouchable && clickListener != null
|
||||||
@@ -159,7 +179,7 @@ class ContentRankingLargeCardView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Int.dpToPx(): Int = (this * resources.displayMetrics.density).roundToInt()
|
private fun Int.dpToPx(scale: Float = 1f): Int = (this * scale * resources.displayMetrics.density).roundToInt()
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val FIGMA_WIDTH = 374
|
const val FIGMA_WIDTH = 374
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.v2.widget.contentranking
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -144,6 +145,83 @@ class ContentRankingCardViewTest {
|
|||||||
assertEquals(View.VISIBLE, view.findViewById<View>(R.id.iv_content_ranking_background).visibility)
|
assertEquals(View.VISIBLE, view.findViewById<View>(R.id.iv_content_ranking_background).visibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `large card는 402dp 미만에서 실제 text size를 축소한다`() {
|
||||||
|
val view = inflateView<ContentRankingLargeCardView>(
|
||||||
|
layoutResId = R.layout.view_content_ranking_large_card,
|
||||||
|
screenWidthDp = 360
|
||||||
|
)
|
||||||
|
|
||||||
|
view.bind(sampleItem(rank = 1))
|
||||||
|
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_rank), 96f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_title), 22f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_creator), 12f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_delta_amount), 16f * 360f / 402f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `medium grid card는 402dp 미만에서 실제 text size를 축소한다`() {
|
||||||
|
val view = inflateView<ContentRankingMediumGridCardView>(
|
||||||
|
layoutResId = R.layout.view_content_ranking_medium_grid_card,
|
||||||
|
screenWidthDp = 360
|
||||||
|
)
|
||||||
|
|
||||||
|
view.setCardSize(ContentRankingCardSize(widthPx = 185, heightPx = 185))
|
||||||
|
view.bind(sampleItem(rank = 2))
|
||||||
|
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_rank), 54f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_title), 22f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_creator), 12f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_delta_amount), 16f * 360f / 402f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `small grid card는 402dp 미만에서 조건부 baseline text size를 축소한다`() {
|
||||||
|
val view = inflateView<ContentRankingSmallGridCardView>(
|
||||||
|
layoutResId = R.layout.view_content_ranking_small_grid_card,
|
||||||
|
screenWidthDp = 360
|
||||||
|
)
|
||||||
|
|
||||||
|
view.setCardSize(ContentRankingCardSize(widthPx = 122, heightPx = 122))
|
||||||
|
view.bind(sampleItem(rank = 8))
|
||||||
|
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_rank), 36f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_title), 14f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_creator), 12f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_delta_amount), 16f * 360f / 402f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `small grid card는 card size 적용 전 bind에서도 402dp 미만 text size를 축소한다`() {
|
||||||
|
val view = inflateView<ContentRankingSmallGridCardView>(
|
||||||
|
layoutResId = R.layout.view_content_ranking_small_grid_card,
|
||||||
|
screenWidthDp = 360
|
||||||
|
)
|
||||||
|
|
||||||
|
view.bind(sampleItem(rank = 8))
|
||||||
|
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_rank), 36f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_title), 14f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_creator), 12f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_delta_amount), 16f * 360f / 402f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `horizontal card는 402dp 미만에서 실제 text size를 축소한다`() {
|
||||||
|
val view = inflateView<ContentRankingHorizontalCardView>(
|
||||||
|
layoutResId = R.layout.view_content_ranking_horizontal_card,
|
||||||
|
screenWidthDp = 360
|
||||||
|
)
|
||||||
|
|
||||||
|
view.bind(sampleItem(rank = 11))
|
||||||
|
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_rank), 40f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_title), 18f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_creator), 14f * 360f / 402f)
|
||||||
|
assertTextSizeSp(view.findViewById(R.id.tv_content_ranking_delta_amount), 16f * 360f / 402f)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `large view holder는 배경 blur와 전경 non blur coverImage를 모두 로드한다`() {
|
fun `large view holder는 배경 blur와 전경 non blur coverImage를 모두 로드한다`() {
|
||||||
val source = projectFile(
|
val source = projectFile(
|
||||||
@@ -161,11 +239,28 @@ class ContentRankingCardViewTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : View> inflateView(layoutResId: Int): T {
|
private inline fun <reified T : View> inflateView(
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
layoutResId: Int,
|
||||||
|
screenWidthDp: Int? = null
|
||||||
|
): T {
|
||||||
|
val baseContext = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
val context = if (screenWidthDp == null) {
|
||||||
|
baseContext
|
||||||
|
} else {
|
||||||
|
baseContext.createConfigurationContext(
|
||||||
|
Configuration(baseContext.resources.configuration).apply { this.screenWidthDp = screenWidthDp }
|
||||||
|
)
|
||||||
|
}
|
||||||
return LayoutInflater.from(context).inflate(layoutResId, null, false) as T
|
return LayoutInflater.from(context).inflate(layoutResId, null, false) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assertTextSizeSp(
|
||||||
|
view: TextView,
|
||||||
|
expectedSp: Float
|
||||||
|
) {
|
||||||
|
assertEquals(expectedSp, view.textSize / view.resources.displayMetrics.scaledDensity, 0.0001f)
|
||||||
|
}
|
||||||
|
|
||||||
private fun assertRankTextBox(
|
private fun assertRankTextBox(
|
||||||
view: TextView,
|
view: TextView,
|
||||||
expectedWidth: Int,
|
expectedWidth: Int,
|
||||||
|
|||||||
Reference in New Issue
Block a user