Tak3r07
Tak3r07

Reputation: 547

Draw a circle onto a view (android)

I am starting with my first attempts to write an android app. I'd like to to visualize the Monte-Carlo-Approximation for pi. Hence I first want to draw a Circle onto a view but i dont get it working! I have tried to create my own "CircleView" Class which extends "View" and overwrite the onDraw(..) method like its explained over here: How to draw circle by canvas in Android?

This is my CircleView Class

public class CircleView extends View {
    public CircleView(Context context) {
        super(context);
    }

    protected void onDraw(Canvas canvas){
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setColor(150);
        canvas.drawCircle(50,50,20,paint);
    }
}

I have inserted the CircleView into a LinearLayout with the following XML-code

<com.tak3r07.montecarlopi.CircleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/circleView"
    android:layout_weight="1"/>

(Btw Android Studio is telling me in the XML-view at the right side: "Rendering Problems Custom view CircleView is not using the 2- or 3-argument View constructors; XML attributes will not work")

The App just crashes with the following log: http://pastebin.com/Gv1GaHtX

Can someone tell what i did wrong?

I thought this setup would create an activity with a view which displays a circle.

Regards

Edit: Crash is fixed by adding the 2 and 3 Parameter Constructor in CircleView (See https://stackoverflow.com/a/13797457/3248708)

But now i still do not see any Circle in the activity

Upvotes: 10

Views: 19507

Answers (2)

smarteist
smarteist

Reputation: 1421

You can create a Circular layout and inside this view, every childs should be rounded up :

public class CircleView extends FrameLayout {

    private Bitmap maskBitmap;
    private Paint paint, maskPaint;


    public CircleView(Context context) {
        super(context);
        init();
    }

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();

    }

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {
        Bitmap offscreenBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas offscreenCanvas = new Canvas(offscreenBitmap);

        super.draw(offscreenCanvas);

        if (maskBitmap == null) {
            maskBitmap = createMask(getWidth(), getHeight());
        }

        offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
        canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
    }

    private Bitmap createMask(int width, int height) {
        Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(mask);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);

        canvas.drawRect(0, 0, width, height, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRoundRect(new RectF(0, 0, width, height), width/2f, height/2f, paint);

        return mask;
    }

}

Upvotes: 0

Michael Krause
Michael Krause

Reputation: 4849

A couple of observations:

You need to take into account the width and height assigned to your view when determining your circle's center point and radius.

You should take into account the padding assigned to your View so you don't draw in that reserved portion.

You should avoid allocating objects within your onDraw method since this gets called a lot.

In order to allow your view to be specified in an XML layout, you need to provide the constructor that takes a Context and an AttributeSet. The AttributeSet is the mechanism by which your XML attributes are passed to your view.

Give this a try:

package com.tak3r07.montecarlopi;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CircleView extends View
{
    private static final int DEFAULT_CIRCLE_COLOR = Color.RED;

    private int circleColor = DEFAULT_CIRCLE_COLOR;
    private Paint paint;

    public CircleView(Context context) 
    {
        super(context);
        init(context, null);
    }

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

    private void init(Context context, AttributeSet attrs)
    {
        paint = new Paint();
        paint.setAntiAlias(true);
    }

    public void setCircleColor(int circleColor)
    {
        this.circleColor = circleColor;
        invalidate();
    }

    public int getCircleColor()
    {
        return circleColor;
    }

    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);

        int w = getWidth();
        int h = getHeight();

        int pl = getPaddingLeft();
        int pr = getPaddingRight();
        int pt = getPaddingTop();
        int pb = getPaddingBottom();

        int usableWidth = w - (pl + pr);
        int usableHeight = h - (pt + pb);

        int radius = Math.min(usableWidth, usableHeight) / 2;
        int cx = pl + (usableWidth / 2);
        int cy = pt + (usableHeight / 2);

        paint.setColor(circleColor);
        canvas.drawCircle(cx, cy, radius, paint);
    }
}

Upvotes: 19

Related Questions