36

I need to be able to access the size of the view's canvas to perform some calculations. For some reason, the size of the view passed to onSizeChanged is different than the size of the canvas passed to onDraw. My current workaround uses a boolean flag to determine when I need to do the calculations.

The ideal solution would allow me to do these calculations in the onSizeChanged method, so I'm wondering... is there any way I can get ahold of the Canvas object (or at least it's dimensions) outside of the onDraw method?

My code is below. It is draws the radius of a circle at a given angle. When I use canvas.centerX() to determine the start points and end points for the radius, everything works perfectly. If I use the parameters passed into onSizeChanged, it isn't even remotely close to correct.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  mSizeChanged = true;
}

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

  if (mSizeChanged) {
    RectF bounds = new RectF(canvas.getClipBounds());
    float centerX = bounds.centerX();
    float centerY = bounds.centerY();
    float radianAngle = (float) Math.toRadians(mStartAngle);

    mRadius[0] = center;
    mRadius[1] = center;
    mRadius[2] = center + center * FloatMath.cos(radianAngle);
    mRadius[3] = center + center * FloatMath.sin(radianAngle);
    mSizeChanged = false;
  }

  mPaint.setColor(0xFF330000);
  mPaint.setStrokeWidth(1);
  canvas.drawLines(mRadius, mPaint);
}
dfetter88
  • 5,381
  • 13
  • 44
  • 55

1 Answers1

39

For drawing purposes, you should not really use the dimensions of the Canvas object.

Just use the dimensions provided to you in the onSizeChanged method. You can either store the dimensions for use in the onDraw method or resize/draw to a backing bitmap that you can draw with later.

Update:

Quickly whipped up some code, it looks like this works:

public class CustomView extends View{
    private Paint paint;
    private int w;
    private int h;

    public CustomView(Context context, AttributeSet attr) {
        super(context, attr);
        paint = new Paint();
        paint.setTextAlign(Align.CENTER);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        this.w = w;
        this.h = h;
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.drawText("TEST", w/2, h/2, paint);   
    }
}

Update 2

Following the circle code update.

We can do this:

   @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        float centerX = (float) w/2;
        float centerY = (float) h/2;
        float radianAngle = (float) Math.toRadians(startAngle);

        radius[0] = centerX;
        radius[1] = centerY;
        radius[2] = centerX + centerX * FloatMath.cos(radianAngle);
        radius[3] = centerY + centerY * FloatMath.sin(radianAngle);

        paint.setColor(0xFF330000);
        paint.setStrokeWidth(1);
        canvas.drawLines(radius, paint);
    }

You'll see that this now works on any sized view.

Che Jami
  • 5,151
  • 2
  • 21
  • 18
  • I would be fine with that, but the onSizeChanged method does not give me proper numbers. For example, suppose I wanted to draw some text in the center of the view. If I store the values passed to onSizeChanged, and use those to determine where the text should be drawn, it is not drawn in the correct spot. If I use the canvas dimensions to determine where the text should be drawn, it works perfectly. – dfetter88 Jul 11 '11 at 15:57
  • 1
    If you compare your w/2 & h/2 values to the results of the methods canvas.centerX() and canvas.centerY(), you'll see that the values are different. – dfetter88 Jul 11 '11 at 16:48
  • When I use your code, the text does not appear at all because w/2 and h/2 give coordinates that are larger than the canvas. Thus, the text is drawn in a place that is not displayed on the screen. – dfetter88 Jul 11 '11 at 16:50
  • The canvas width and height will give you the screen width and height. Don't you want to use the dimensions of your view (given in onSizeChanged)? It'd be nice if you could reveal some more of your drawing code to help us pin-point the issue. – Che Jami Jul 11 '11 at 17:02
  • I've updated the OP with some more code. I would love to be able to use the view's dimensions, but it's not working for me. – dfetter88 Jul 11 '11 at 17:26
  • Let me know if the updated code gives the desired behaviour. Are you making a clock or something? :P – Che Jami Jul 11 '11 at 20:14
  • It only works when no padding has been defined in the parent viewgroup (FrameLayout in this case). Unfortunately that's not good enough for my current project. No matter what I try, I can't get it to work properly unless I use the canvas's clip bounds. – dfetter88 Jul 12 '11 at 01:12
  • I placed the CustomView within a padded FrameLayout and it seems to be working fine. We may be missing something here... – Che Jami Jul 12 '11 at 08:32
  • Thank you for all of your help! You've led me down the right path. The problem was how I was setting the dimensions in my `onMeasure` method. I had a "preferred" size of `300dip`, but when this size was selected, the problems began showing up. My solution was to create new `MeasureSpec`'s with the selected size, and pass those to the `super.onMeasure` method. Once again, thanks... If it wasn't for your help, I would've given up on this some time ago. Now your method works perfectly! – dfetter88 Jul 12 '11 at 12:18