Lollipop elevation on concave outline
As some of the comments and answer point out, native android shadow only works with convex outlines.
So you are left with either drawing a fake shadow manually on your own (via canvas, bitmap etc) or rely on someone else's library to draw fake shadow for you (Google's Material Components library etc).
Are there any known workarounds or any other creative solution that any of you know?
If you must rely on native android shadow, you can try to break down the shape into multiple convex shape and draw these individually.
Here is an example:
I broke down the star shape into 1 pentagon and 5 triangle polygons (all of them have convex outline) and draw them individually.
TriangleView:
public class TriangleView extends View {
private final Path path = new Path();
private final Paint paint = new Paint();
public TriangleView(Context context) {
super(context);
init(context, null, 0,0);
}
public TriangleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0,0);
}
public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(255, 100, 100, 255));
setOutlineProvider(new OutlineProvider());
}
public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3){
path.reset();
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
path.close();
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
private static class OutlineProvider extends ViewOutlineProvider{
@Override
public void getOutline(View view, Outline outline) {
Path path = ((TriangleView)view).path;
outline.setConvexPath(path);
}
}
}
PentagonView:
public class PentagonView extends View {
private final Path path = new Path();
private final Paint paint = new Paint();
public PentagonView(Context context) {
super(context);
init(context, null, 0,0);
}
public PentagonView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0,0);
}
public PentagonView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public PentagonView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(255, 150, 150, 255));
setOutlineProvider(new OutlineProvider());
}
public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float x5, float y5){
path.reset();
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
path.lineTo(x4, y4);
path.lineTo(x5, y5);
path.close();
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
private static class OutlineProvider extends ViewOutlineProvider {
@Override
public void getOutline(View view, Outline outline) {
Path path = ((PentagonView)view).path;
outline.setConvexPath(path);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<app.eccweizhi.concaveshadow.PentagonView
android:id="@+id/pentagonView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="@+id/triangle1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="@+id/triangle2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="@+id/triangle3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="@+id/triangle4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="@+id/triangle5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
</FrameLayout>
I then use them like this
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pentagonView.setPoints(
520f,
520f,
640f,
520f,
677.0818f,
634.1266f,
580f,
704.6608f,
482.9182f,
634.1266f
)
triangle1.setPoints(520f, 520f, 640f, 520f, 580f, 400f)
triangle2.setPoints(640f, 520f, 677.0818f, 634.1266f, 777f, 520f)
triangle3.setPoints(677.0818f, 634.1266f, 580f, 704.6608f, 697f, 750f)
triangle4.setPoints(580f, 704.6608f, 482.9182f, 634.1266f, 440f, 750f)
triangle5.setPoints(482.9182f, 634.1266f, 520f, 520f, 400f, 520f)
}
}