To my surprise I've just discovered that drawLine and drawRect don't include the ending position, i.e.:
canvas.drawLine(100, 100, 100, 100, paint);
or
RectF rect = new RectF(100, 100, 100, 100);
canvas.drawRect(rect, paint);
won't draw anything.
My paint is defined as follows:
Paint paint = new Paint();
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.FILL);
return paint;
I've tried defining my paint as FILL_AND_STROKE, but it wouldn't help.
Android's drawPaint() javadoc don't even list the stopX and stopY params!
So, if I want to draw a vertical line that goes exactly from beginY to endY (inclusive), I have to do the following:
canvas.drawLine(constX, beginY, constX, endY + 1)
Notice that I didn't add 1 to the ending X position, only to ending Y (xstays the same as I want a vertical line).
My device is HTC SENSE.
Edit: Simon, you're right, rather than asking a question I've just tried to share my feeling of surprise that Android does not do what its docs say in such a fundamental case as basic drawing, and make sure that I didn't do any stupid mistake on my way.
To make myself clearer: drawRect's javadoc says:
public void drawRect (float left, float top, float right, float bottom, Paint paint)
Draw the specified Rect using the specified paint. The rectangle will be filled or framed based on the Style in the paint.
left - The left side of the rectangle to be drawn
top - The top side of the rectangle to be drawn
right - The right side of the rectangle to be drawn
bottom - The bottom side of the rectangle to be drawn
paint - The paint used to draw the rect
So, when writing
canvas.drawRect(x1, y1, x2, y2)
You expect a rectangle whose corners are at (x1, y1); (x1, y2); (x2, y1) and (x2, y2).
Android says: wrong! They will be at (x1, y1); (x1, y2-1); (x2-1, y1) and (x2-1, y2-1).
For the curious ones: set the canvas clipping:
canvas.clipRect(x1, y1, x2, y2)
Then try to draw a point:
canvas.drawPoint(x1, y1, paint);
and you get a point on the screen.
Then try in the opposite corner:
canvas.drawPoint(x2, y2, paint);
nothing appears. nothing will appear in the remaining 2 corners as well:
canvas.drawPoint(x1, y2, paint);
canvas.drawPoint(x2, y2, paint);
Still not surprising to you folks?
So the conclusion is that Android treats right and bottom coordinates as exclusive, meaning that e.g. when writing:
canvas.clipRect(x1, y1, x2, y2)
You will get the clipping bounds of (x1, y1, x2 - 1, y2 - 1). The same goes with every method that takes right and bottom coordinates or Rect/RectF objects.
Your question is essentially the answer for this. Thank you.
I, for one, am not surprised by this, and wouldn't want it any other way. Here's why:
It is consistent with
java.awt
,javax.swing
and other APIs, as well as core Java methods such asString.substring(int, int)
andList<?>.get(int)
.It is compatible with
android.graphics.RectF
, which doesn't care about pixels—how can you have a fraction of a pixel?The API is convenient:
For a 40×30 rectangle, instantiate
Rect(0, 0, 40, 30)
Rect.right - Rect.left == Rect.width()
The maths and geometry are easier this way. For example, if you want draw a rectangle centred within the canvas:
The API is right; the API documentation, unfortunately, just lacks detail.
(Actually, it is mentioned in the detail for the
Rect.contains(int, int)
method. It's unfortunate that Google let the API out the door without the field variables themselves being documented, however.)if your background is black ,add one statement
paint.setColor(Color.RED);
and also you had passRectF rect = new RectF(100, 100, 100, 100);
all points are same , tryRectF rect = new RectF(100, 100, 200, 200);
OR for line
canvas.drawLine(10, 10, 10, 10 + 15, paint);
Your rect should work fins, try to add some background, your line is now kust a pixel, the two last parameter is not the length, its the end posision.
Your question reveals an inconsistency in the drawing API of Android. You say
The sentence in brackets is in my opinion the key to understand the nature of the inconsistency:
You also could add 1 to the ending X position (or even to the beginning X position!), and you would get exactly the pixel-wise identical line. Why is that? Because the underlying algorithm to transform from Android's "left pixel in/right pixel out"-concept to the "start and end pixel in"-concept is as follows (shown only for x, for y it's the same):
The result of this (in my opinion sub-optimal) conversion is that the line
is exactly parallel to
I think everybody would expect kind of inverse V, since the end points are separated by at least 1 pixel (their distance being two pixels) and the starting points are identical.
But tested on various devices and Android versions, the 1st perfectly vertical line is in pixel column 149 and the 2nd in column 150.
BTW: The correct transformation to use the "start and end pixel in"-concept is: