首页 文章

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

提问于
浏览 1353
14

令我惊讶的是,我刚刚发现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)

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

我的设备是HTC SENSE .

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

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

public void drawRect(float left,float top,float right,float bottom,Paint paint)使用指定的绘制绘制指定的Rect . 矩形将根据绘画中的样式填充或加框 . left - 要绘制的矩形的左侧顶部 - 要绘制的矩形的顶部右侧 - 要绘制的矩形的右侧底部 - 要绘制的矩形的底部绘制 - 用于绘制的矩形绘制矩形

所以,写作时

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

没有出现 . 其余2个角落中也不会出现任何内容:

canvas.drawPoint(x1, y2, paint);


canvas.drawPoint(x2, y2, paint);

你们这些人还不奇怪吗?

因此,结论是Android将 rightbottom 坐标视为独占,意味着例如写作时:

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

您将获得(x1,y1,x2 - 1,y2 - 1)的剪切边界 . 每个采用 rightbottom 坐标或 Rect/RectF 个对象的方法都是如此 .

4 回答

  • 5

    您的问题揭示了Android绘图API的不一致性 . 你说

    所以,如果我想绘制一条完全从beginY到endY(包括)的垂直线,我必须执行以下操作:canvas.drawLine(constX,beginY,constX,endY 1)
    请注意,我没有将1添加到结束X位置,只添加到结尾Y(xstays与我想要的垂直线相同) .

    括号中的句子在我看来是理解不一致性质的关键:

    您还可以将1添加到结束X位置(或甚至添加到起始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的类型,因为 endpoints 相隔至少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

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

  • 0

    如果你的背景是黑色,添加一个语句 paint.setColor(Color.RED); 并且你也传递 RectF rect = new RectF(100, 100, 100, 100); 所有点都相同,试试 RectF rect = new RectF(100, 100, 200, 200);

    或者对于行 canvas.drawLine(10, 10, 10, 10 + 15, paint);

  • 0

    你的问题基本上是this的答案 . 谢谢 .

    我,对此,我并不感到惊讶,也不会想要任何其他方式 . 原因如下:

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

    • 它与 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中提到 . 不幸的是,谷歌在没有记录字段变量的情况下让API出了门 . )

相关问题