Reputation: 8670
My usecase is to edit shapeAppearanceModel for com.google.android.material.card.MaterialCardView
card.shapeAppearanceModel = card.shapeAppearanceModel
.toBuilder()
.setTopEdge(TriangleEdgeTreatment(triangleSize))
.build()
Above code is working as expected But due to above programatically setup of shapeAppearanceModel
<com.google.android.material.card.MaterialCardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="?myCustomColor"
app:cardCornerRadius="8dp"
app:cardElevation=4dp"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/myImageDrawable"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.google.android.material.card.MaterialCardView>
In Android studio as the TopEdge is define programatically Editor is not replicating the bug but in Emulator/Real device the child is not getting clipped and The Rounded Corners are getting overlapped by ImageView.
And when i remove the shapeAppearanceModel setup the clipping work's as expected.
Upvotes: 5
Views: 1646
Reputation: 301
The reason is Because MaterialCardView
locked clipToOutline
behaviour only whe the Shape is Marked as Rounded and unfortunately, that implementation also locked by the component it self by default :
here if you open MaterialCardView.java, you can find this code:
@Override
public void setShapeAppearanceModel(@NonNull ShapeAppearanceModel shapeAppearanceModel) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
setClipToOutline(shapeAppearanceModel.isRoundRect(getBoundsAsRectF()));
}
cardViewHelper.setShapeAppearanceModel(shapeAppearanceModel);
}
and if you go deeper, the implementation of isRoundRect
, would be like this :
@RestrictTo(LIBRARY_GROUP)
public boolean isRoundRect(@NonNull RectF bounds) {
boolean hasDefaultEdges =
leftEdge.getClass().equals(EdgeTreatment.class)
&& rightEdge.getClass().equals(EdgeTreatment.class)
&& topEdge.getClass().equals(EdgeTreatment.class)
&& bottomEdge.getClass().equals(EdgeTreatment.class);
float cornerSize = topLeftCornerSize.getCornerSize(bounds);
boolean cornersHaveSameSize =
topRightCornerSize.getCornerSize(bounds) == cornerSize
&& bottomLeftCornerSize.getCornerSize(bounds) == cornerSize
&& bottomRightCornerSize.getCornerSize(bounds) == cornerSize;
boolean hasRoundedCorners =
topRightCorner instanceof RoundedCornerTreatment
&& topLeftCorner instanceof RoundedCornerTreatment
&& bottomRightCorner instanceof RoundedCornerTreatment
&& bottomLeftCorner instanceof RoundedCornerTreatment;
return hasDefaultEdges && cornersHaveSameSize && hasRoundedCorners;
}
there's a condition to make it return true will be if the shape has the same or all corner rounded.
You can't trick that by programmatically set card.setClipToOutline(true)
because it happended after the render proses.
The reason they make it that way because of some limitation by design :
(further reading : https://github.com/material-components/material-components-android/issues/1950)
The Good News you can solve that by creating your own MaterialCardView
and override some implementation like this :
package com.example.myapp;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.shape.ShapeAppearancePathProvider;
public class MaskedCardView extends MaterialCardView {
private ShapeAppearancePathProvider pathProvider = new ShapeAppearancePathProvider();
private Path path = new Path();
private RectF rectF = new RectF(0f, 0f, 0f, 0f);
public MaskedCardView(Context context) {
super(context);
}
public MaskedCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MaskedCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.clipPath(path);
super.onDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
rectF.right = (float) w;
rectF.bottom = (float) h;
pathProvider.calculatePath(getShapeAppearanceModel(), 1f, rectF, path);
super.onSizeChanged(w, h, oldw, oldh);
}
}
I hope it can answer your question.
Upvotes: 3