画布'drawLine和drawRect不包括结束位置?

时间:2012-01-22 22:02:51

标签: android drawing paint android-canvas

令我惊讶的是,我刚刚发现drawLine和drawRect不包括结束位置,即:

canvas.drawLine(100, 100, 100, 100, paint);

RectF rect = new RectF(100, 100, 100, 100);
canvas.drawRect(rect, paint);

不会画任何东西。

我的画作定义如下:

Paint paint = new Paint();
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.FILL);
return paint;

我尝试将我的绘画定义为FILL_AND_STROKE,但它无济于事。

Android的drawPaint()javadoc甚至没有列出stopX和stopY参数!

所以,如果我想绘制一条完全从beginY到endY(包括)的垂直线,我必须执行以下操作:

canvas.drawLine(constX, beginY, constX, endY + 1)

请注意,我没有在结尾的X位置添加1,只是结束Y(xstays与我想要的垂直线相同)。

我的设备是HTC SENSE。

编辑:西蒙,你是对的,而不是问一个问题,我只是试图分享我的惊讶,因为Android在基本绘图这样的基本情况下没有做文档所说的,并确保我在途中没有犯任何愚蠢的错误。

让自己更清楚:drawRect的javadoc说:

  

public void drawRect(向左浮动,浮动顶部,向右浮动,浮动底部,油漆颜料)

     

使用指定的绘制绘制指定的Rect。矩形将根据绘画中的样式填充或加框。

     

left - 要绘制的矩形的左侧

     

top - 要绘制的矩形的顶边

     

右 - 要绘制的矩形的右侧

     

bottom - 要绘制的矩形的底边

     

paint - 用于绘制矩形的绘画

所以,写作时

canvas.drawRect(x1, y1, x2, y2)

你期望一个角落在(x1,y1)的矩形; (x1,y2); (x2,y1)和(x2,y2)。

Android说:错了!他们将在(x1,y1); (x1,y2-1); (x2-1,y1)和(x2-1,y2-1)。

对于好奇的:设置画布剪辑:

canvas.clipRect(x1, y1, x2, y2)

然后尝试画出一点:

canvas.drawPoint(x1, y1, paint);

你在屏幕上得到一个点。

然后尝试对角:

canvas.drawPoint(x2, y2, paint);
什么也没出现。其余两个角落也不会出现任何内容:

canvas.drawPoint(x1, y2, paint);


canvas.drawPoint(x2, y2, paint);

你们这些人还不奇怪吗?

因此,结论是Android将正确底部坐标视为独占,这意味着,例如写作时:

canvas.clipRect(x1, y1, x2, y2)

您将获得(x1,y1,x2 - 1,y2 - 1)的限幅范围。采用正确底部坐标或 Rect / RectF 对象的每种方法都是如此。

4 个答案:

答案 0 :(得分:5)

您的问题显示Android的绘图API存在不一致。你说

  

所以,如果我想绘制一条完全从beginY到endY(包括)的垂直线,我必须执行以下操作:

canvas.drawLine(constX, beginY, constX, endY + 1)
     

请注意,我没有在结尾的X位置添加1,只是结束Y(xstays与我想要的垂直线相同)。

括号中的句子是我认为理解不一致性质的关键:

你也可以在结尾的X位置加上1(或者甚至到X的开头位置!),你就可以得到与像素相同的完全相同的线。这是为什么?因为从Android的“左像素输入/右像素输出”转换为“开始和结束像素输入”的概念的基础算法如下所示(仅针对x显示,y表示相同):

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

这个(在我看来次优)转换的结果是行

canvas.drawLine(150, 150, 149, 160, paint);

完全平行
canvas.drawLine(150, 150, 151, 160, paint);

我认为每个人都会期望反V型,因为端点之间至少有1个像素(它们的距离是两个像素)并且起点是相同的。

但在各种设备和Android版本上进行了测试,第一条完美垂直线位于像素列149中,第二列位于第150列。

BTW:正确的转换使用“开始和结束像素” - 概念是:

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

答案 1 :(得分:1)

您的问题基本上是this的答案。谢谢。

对于其中之一,我并不感到惊讶,并且不会想要任何其他方式。原因如下:

  • 它与java.awtjavax.swing和其他API以及String.substring(int, int)List<?>.get(int)等核心Java方法一致。

    < / LI>
  • 它与android.graphics.RectF兼容,它不关心像素 - 你如何拥有像素的一小部分?

  • API很方便:

    • 对于40×30的矩形,请实例化Rect(0, 0, 40, 30)

    • Rect.right - Rect.left == Rect.width()

  • 通过这种方式,数学和几何更容易。例如,如果要绘制在画布中居中的矩形:

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

API是对的;遗憾的是,the API documentation缺乏细节。

(实际上,the detail for the Rect.contains(int, int) method中提到了这一点。不幸的是,Google在没有记录字段变量的情况下让API出了门。)

答案 2 :(得分:0)

如果您的背景为黑色,请添加一条语句paint.setColor(Color.RED); 你也通过了RectF rect = new RectF(100, 100, 100, 100); 所有点都相同,请尝试RectF rect = new RectF(100, 100, 200, 200);

OR行canvas.drawLine(10, 10, 10, 10 + 15, paint);

答案 3 :(得分:0)

你的矩形应该工作鳍,尝试添加一些背景,你的线现在是一个像素,最后两个参数不是长度,它的结束位置。