MaterialCardView clips its children when using rounded corners. If I use cardCornerRadius = true (which rounds all 4 corners of the card), the clipping behaves as expected -> children are not being drawn outside of the rounded area. If I try to round less corners (ie. only top corners, using ShapeAppearanceModel), then the clipping part is lost -> a top positioned child will be drawn over the rounded cornered area).
I realised that clip MaterialCardView calls
setClipToOutline(shapeAppearanceModel.isRoundRect(getBoundsAsRectF()));
inside of setShapeAppearanceModel, where isRoundRect returns true only if all four corners are rounded, so I tried applying clipToOutline = true on the MaterialCardView after setting the shapeAppearanceModel with top corners rounded, but with no similar result - children were still able to be drawn over the rounded part of the parent card.
What is actually triggering the clipping part and how can I force it on a top rounded shaped MaterialCardView?
LE: trial and error code:
// 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()
After debugging the MaterialCardView source code I came up with the following sub-class that will prevent from additional offset to padding and unclipping to outlines.
/**
* 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 AsymmetricRadiusCardView @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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With