带有顶部圆角的MaterialCardView应该剪掉子项

问题描述 投票:0回答:2
使用圆角时,

MaterialCardView
会剪辑其子项。如果我使用
cardCornerRadius = true
(将卡片的所有 4 个角圆化),剪切行为将按预期进行 -> 子对象不会被绘制到圆角区域之外。如果我尝试减少圆角(即仅顶角,使用
ShapeAppearanceModel
),则剪切部分会丢失 -> 顶部定位的子项将绘制在圆角区域上)。

我意识到剪辑MaterialCardView调用

setClipToOutline(shapeAppearanceModel.isRoundRect(getBoundsAsRectF()));
setShapeAppearanceModel
内部,其中
isRoundRect
仅当所有四个角都是圆角时才返回 true,因此我尝试在将
clipToOutline = true
设置为圆角后在
MaterialCardView
上应用
shapeAppearanceModel
,但没有类似的结果 - 儿童仍然能够在父卡的圆形部分上绘制。

实际触发剪切部分的是什么以及如何将其强制到顶部圆形 MaterialCardView 上?

LE:尝试错误代码:

// card is MaterialCardView
card.shapeAppearanceModel =   
    ShapeAppearanceModel()
        .toBuilder()
        .setTopRightCorner(CornerFamily.ROUNDED, cornerPx) // cornerPx = 24dp in pixels
        .setTopLeftCorner(CornerFamily.ROUNDED, cornerPx)
        .build()

card.apply {
    preventCornerOverlap = true
    clipChildren = true
    clipToOutline = true
}
card.invalidateOutline()
    
android android-cardview material-components-android materialcardview
2个回答
0
投票

如果您使用形状外观叠加,请应用以下配置以防止子项从角重叠:

  • cardPreventCornerOverlap
    设置为 true
  • 然后将与角设置有关的所有其他内容保留为默认值(只需在设计模式下从文本框中删除值)。

然后你应该看到类似这样的内容:


0
投票

调试 MaterialCardView 源代码后,我想出了以下子类,它将防止填充的额外偏移和取消轮廓的剪裁。

/**
 * This view is for solving the MaterialCardView bug that in case that not all corner radius are of the same size
 * an additional offset is added to the card padding which causes undesired behavior(e.g certain child view of the card
 * that is aligned to the card top wont get it's corners rounded according to the card corner radius which will make it look like
 * it overlaps the card border at the corners).
 * Note that this bug does not occur when the corner radius is the same size for all corners!!!
 * [For more info](https://stackoverflow.com/questions/71824566/materialcardview-with-top-rounded-corners-should-clip-children)
 *
 * @constructor
 *
 * @param context
 * @param attributeSet
 * @param defStyleRes
 */
class AsymetricRadiusCardView @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleRes: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attributeSet, defStyleRes) {

    /*
    Method instance of MaterialCardView.setAncestorContentPadding via reflection.
    Invoking this method will allow us to set desired card content padding without
    the additional offset that is added when calling MaterialCardView.setContentPadding if
    the radius of all corners is not equal.
     */
    private val reflectedAncestorPaddingMethod: Method by lazy {
        MaterialCardView::class.java.getDeclaredMethod(
            "setAncestorContentPadding",
            Int::class.java,
            Int::class.java,
            Int::class.java,
            Int::class.java
        ).apply {
            isAccessible = true
        }
    }

    init {
        setContentPadding(contentPaddingLeft, contentPaddingTop, contentPaddingRight, contentPaddingBottom)
    }

    override fun setContentPadding(left: Int, top: Int, right: Int, bottom: Int) {
        //override this method so that after calling super method for content padding which
        //will add the undesired padding offset in case the radius is not the same size for all corners
        //we will invoke the reflected to enforce given padding without additional offset
        super.setContentPadding(left, top, right, bottom)
        try {
            reflectedAncestorPaddingMethod.invoke(this, left, top, right, bottom)
        } catch (e: Exception) {
            Timber.w(e)
        }
    }

    override fun setShapeAppearanceModel(shapeAppearanceModel: ShapeAppearanceModel) {
        //due to the fact that in super method if the radius is not the same size for all corners
        //the card wont be clipped to outline(which can cause overlap drawing of it's child views on card corners)
        //we enforce clipping to outline after calling super method
        super.setShapeAppearanceModel(shapeAppearanceModel)
        clipToOutline = true
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.