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()
调试 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
}
}