Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw segment of circle at the bottom of view [android]?

My activity should look like this:

I'm not sure what's the best way to show the segment of the circle between top and bottom views.

I'd like to avoid saving the segment as image.

Is there a way to show it using drawable xml (I've tried to achieve this using oval, but I don't know how to make is so "flat")?

Maybe drawing on canvas?

like image 341
Vasily Kabunov Avatar asked Mar 11 '17 11:03

Vasily Kabunov


1 Answers

I think option 3 is the best, you can control exactly what you're drawing but you don't loose OOP karma points

OPTION 1

You can create a custom View for your top View and implement the drawing yourself like this, I'm subclassing RelativeLayout here but you can subclass whatever 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 an xml drawable as a background for your top View, the only problem here is that the horizontal stretch of the circle is given with a size in dp, so it's not relative to the View's size

<?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 set it programmatically

myView.setBackground(new CurveBottomDrawable(0xffff0000));
like image 50
lelloman Avatar answered Oct 22 '22 04:10

lelloman