Canvas' drawLine and drawRect not including en

2020-06-08 19:06发布

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.

4条回答
Evening l夕情丶
2楼-- · 2020-06-08 19:40

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.)

查看更多
等我变得足够好
3楼-- · 2020-06-08 19:44

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);

查看更多
一夜七次
4楼-- · 2020-06-08 19:47

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.

查看更多
SAY GOODBYE
5楼-- · 2020-06-08 19:54

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);
查看更多
登录 后发表回答