I think option 3 is the best, you can precisely control what you draw, but you do not lose the karma points of OOP.
OPTION 1
You can create a custom View for your top View and implement the drawing yourself, like this, I will subclass RelativeLayout here, but you can subclass all View you want
public class CurveBgRelativeLayout extends RelativeLayout { public CurveBgRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } private Path path; private Paint paint; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL); paint.setColor(0xffff0000); path = new Path(); float horizontalOffset = w * .8f; float top = -h * .8f; float bottom = h; RectF ovalRect = new RectF(-horizontalOffset, top, w + horizontalOffset, bottom); path.lineTo(ovalRect.left, top); path.arcTo(ovalRect, 0, 180, false); path.setFillType(Path.FillType.INVERSE_EVEN_ODD); } @Override protected void onDraw(Canvas canvas) { if (path != null) canvas.drawPath(path, paint); super.onDraw(canvas); } }
and then in your xml layout
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.CurveBgRelativeLayout android:layout_width="match_parent" android:background="#ffffffff" android:layout_height="0dp" android:layout_weight="1"/> <View android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="#ffff0000" /> </LinearLayout>
OPTION 2
You can create xml as the background for the top View , the only problem here is that the horizontal stretch of the circle is set to the size in dp, so it does not apply to the size of the View
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:height="40dp" android:gravity="bottom"> <shape android:shape="rectangle"> <solid android:color="#ffff0000" /> </shape> </item> <item android:width="500dp" android:height="60dp" android:gravity="bottom|center_horizontal" android:top="-40dp"> <shape android:shape="oval"> <solid android:color="#ffffffff" /> </shape> </item> <item android:height="30dp" android:bottom="30dp" android:gravity="bottom"> <shape android:shape="rectangle"> <solid android:color="#ffffffff" /> </shape> </item> </layer-list>
OPTION 3
public class CurveBottomDrawable extends Drawable { private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Path path = new Path(); public CurveBottomDrawable(int color) { paint.setStyle(Paint.Style.FILL); paint.setColor(color); } @Override public void draw(@NonNull Canvas canvas) { paint.setStyle(Paint.Style.FILL); paint.setColor(0xffff0000); path.reset(); Rect bounds = getBounds(); float horizontalOffset = bounds.width() * .8f; float top = -bounds.height() * .8f; float bottom = bounds.height(); RectF ovalRect = new RectF(-horizontalOffset, top, bounds.width() + horizontalOffset, bottom); path.lineTo(ovalRect.left, top); path.arcTo(ovalRect, 0, 180, false); path.setFillType(Path.FillType.INVERSE_EVEN_ODD); canvas.drawPath(path, paint); } @Override public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { paint.setAlpha(alpha); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { paint.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSPARENT; } }
and then install it programmatically
myView.setBackground(new CurveBottomDrawable(0xffff0000));