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);
if your background is black ,add one statement paint.setColor(Color.RED);
and also you had pass RectF rect = new RectF(100, 100, 100, 100);
all points are same , try RectF rect = new RectF(100, 100, 200, 200);
OR for line canvas.drawLine(10, 10, 10, 10 + 15, paint);
Your question reveals an inconsistency in the drawing API of Android. You say
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).
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):
int left, top, right, bottom; // left/top pixel inclusive, right/bottom pixel exclusive
int x1, y1, x2, y2; // x1/y1 and x2/y2 pixels inclusive
if ( left == right ) {
x1 = x2 = left;
} else if ( left < right ) {
x1 = left;
x2 = right - 1;
} else {
x1 = right;
x2 = left - 1;
}
The result of this (in my opinion sub-optimal) conversion is that the line
canvas.drawLine(150, 150, 149, 160, paint);
is exactly parallel to
canvas.drawLine(150, 150, 151, 160, paint);
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:
int x1, y1, x2, y2; // x1/y1 and x2/y2 pixels inclusive
if ( x1 <= x2 )
++x2;
else
++x1;
if ( y1 <= y2 )
++y2;
else
++y1;
canvas.drawLine(x1, y1, x2, y2, 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 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 as String.substring(int, int)
and List<?>.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:
Rect clipBounds = canvas.getClipBounds() int myRectWidth = 20; int myRectHeight = 10; int left = (clipBounds.width() - myRectWidth) / 2; int top = (clipBounds.height() - myRectHeight) / 2; int right = clipBounds.width() - left; // See how nice this is? int bottom = clipBounds.height() - top; // This wouldn't work if bottom was inclusive! canvas.drawRect(left, top, right, bottom);
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.)