Search code examples
androidgeometryandroid-custom-view

Drawing a square bound by a circle


I'm trying to make a custom view, basically a filled circle that has a bounded square view in it which wraps around some text. So I've created a CircleRelativeLayout which nests CircleHolderRelativeLayout which holds the text. CircleHolderRelativeLayout has the width of (parent_width/2)*sqrt(2) - for bounds.

public class CircleRelativeLayout extends RelativeLayout {

private int circleColor = Color.TRANSPARENT;
private Paint drawPaint;

private int mSize;

public CircleRelativeLayout(Context context) {
    super(context);
}

public CircleRelativeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs, 0);
}

public CircleRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, 0);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CircleRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    init(context, attrs, 0);
}

private void setupPaint() {
    drawPaint = new Paint();
    drawPaint.setColor(circleColor);
    drawPaint.setAntiAlias(true);
    drawPaint.setStyle(Paint.Style.FILL);
}

private void init(Context context, AttributeSet attrs, int defStyle) {
    TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleView, 0, 0);
    circleColor = ta.getColor(R.styleable.CircleView_circleColor, 0);
    ta.recycle();
    setupPaint();
}

@Override
protected void dispatchDraw(Canvas canvas) {
    canvas.drawCircle(Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, drawPaint);
}

public void setCircleColor(int newColor){
    //update the instance variable
    circleColor=newColor;
    //redraw the view
    invalidate();
    requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
    int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
    int required = Math.min(originalWidth, originalHeight);
    super.onMeasure(
            MeasureSpec.makeMeasureSpec(required, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(required, MeasureSpec.EXACTLY));
}
}

and

    public class CircleHolderRelativeLayout extends RelativeLayout {
public CircleHolderRelativeLayout(Context context) {
    super(context);
}

public CircleHolderRelativeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CircleHolderRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CircleHolderRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int size = (int)((Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec))/2)*(Math.sqrt(2)));

    super.onMeasure(MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
}
    }

and the xml is

<ro.cloud.onebox.ui.layout.CircleRelativeLayout
    android:id="@+id/circleView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    custom:circleColor="@color/status_new"
    android:layout_margin="@dimen/main_margin">
    <ro.cloud.onebox.ui.layout.CircleHolderRelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:background="@color/colorAccent">
        <ro.cloud.onebox.ui.text.CustomTextViewNormal
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textColor="@color/primary_text_color_dark"
            android:gravity="center"
            android:layout_centerInParent="true"
            android:text="1asdasdasasdasdasdd2"/>
    </ro.cloud.onebox.ui.layout.CircleHolderRelativeLayout>
</ro.cloud.onebox.ui.layout.CircleRelativeLayout>

The issue is that the Circle is drawn over the layouts children. Hence I cannot see the text. If I change the paint to STROKE I can clearly see the text is there. Help :)


Solution

  • Change your code

     @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.drawCircle(Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, drawPaint);
    }
    

    to

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.drawCircle(Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, Math.min(this.getWidth(), this.getHeight()) / 2, drawPaint);
        for(int i = 0; i<getChildCount(); i++) {
            getChildAt(i).draw(canvas);
        }
    }
    

    This will show your text.