canvas 绘图画出的线条粗细不一致

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了canvas 绘图画出的线条粗细不一致相关的知识,希望对你有一定的参考价值。

参考技术A 同情况,重装了驱动无用,然后我很无奈地跟不用压感的值笔死磕,过了几天它居然莫名奇妙地好了OTL(喂你这种死拖等好的办法根本就是在误导别人吧)...

text 为什么Android上Canvas画出的图形不够平滑

为什么 Android 上 Canvas 画出的图形不够平滑?
2016年06月12日 21:49:55 AItsuki 阅读数:3278
原文链接 : Android: Why your Canvas shapes aren’t smooth
原文作者 : Ali Muzaffar
译文出自 : 掘金翻译计划
译者 : Sausure
校对者:zhangzhaoqi, lovexiaov
通过 Google 搜索我们很快就能找到这个在 StackOverflow 中被问了很多次的问题,同时答案也经常是相同的:你需要给你的 Paint 对象设置 ANTI_ALIAS_FLAG 属性。但对于大多数人来说这并不能解决问题。下面我讲讲原因。

在 Canvas 上绘制
若你需要在 Canvas 上绘制,你有两种选择。

直接在 Canvas 上绘制。
先在 Bitmap 上绘制再将 Bitmap 绘制到 Canvas 上。
直接在 Canvas 上绘制
在你绘制前,先设置 Paint 对象的 ANTI_ALIAS_FLAG 属性可以得到平滑的图形。

你有两种设置 ANTI_ALIAS_FLAG 属性的方式:

    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    //或者
    Paint p = new Paint();
    p.setAntiAlias(true);
1
2
3
4
然后通过下面代码直接在 Canvas 上绘制。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(mLeftX + 100, mTopY + 100, 100, p);
    }
1
2
3
4
5


直接在 Canvas 上绘制
正如你看到的,设置 ANTI_ALIAS_FLAG 属性可以产生平滑的边缘。这里它能起作用是因为默认下每当 onDraw 被调用时系统先将 Canvas 清空然后重绘所有东西。当我在下文详细讨论 ANTI_ALIAS_FLAG 的工作原理时, 你会意识到这段信息的重要性。

先在 Bitmap 上绘制再将 Bitmap 绘制到 Canvas 上
如果你需要保存这张被绘制的图形,或者你需要绘制透明的像素,有个很好的办法是先将图形绘制到 Bitmap 上然后再将 Bitmap 绘制到 Canvas 上。下面我们通过代码来实现它。

注意: 在 onDraw 方法中初始化 Bitmap 并不是一个好主意,但在这里可以增加代码可读性。

    Paint p = new Paint();
    Bitmap bitmap = null;
    Canvas bitmapCanvas = null;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (bitmap == null) {
            bitmap = Bitmap.createBitmap(200,
                                         200,
                                         Bitmap.Config.ARGB_8888);
            bitmapCanvas = new Canvas(bitmap);
            bitmapCanvas.drawColor(
                           Color.TRANSPARENT,
                           PorterDuff.Mode.CLEAR);
        }
        drawOnCanvas(bitmapCanvas);
        canvas.drawBitmap(bitmap, mLeftX, mTopY, p);

    }

    protected void drawOnCanvas(Canvas canvas) {
        canvas.drawCircle(mLeftX + 100, mTopY + 100, 100, p);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
此方式实现效果如下,没有设置 ANTI_ALIAS_FLAG 的图像不够平滑,而设置了该属性的更好一点,但你还是能发现它的边缘是粗糙的。



先在 Bitmap 上绘制再将 Bitmap 绘制到 Canvas 上
上面的代码有什么错误?
我们很容易会忽视上面代码片段出现的问题。即虽然每次 onDraw 被调用时都会更新你在 Bitmap 上绘制的圆形,但理论上说,你只是在上一个图片上重绘。所以这个问题的答案是 ANTI_ALIAS_FLAG 到底是怎么工作的?

ANTI_ALIAS_FLAG 是怎么工作的?
简单来说,ANTI_ALIAS_FLAG 通过混合前景色与背景色来产生平滑的边缘。在我们的例子中,背景色是透明的而前景色是红色的,ANTI_ALIAS_FLAG 通过将边缘处像素由纯色逐步转化为透明来让边缘看起来是平滑的。

而当我们在 Bitmap 上重绘时,像素的颜色会越来越纯粹导致边缘越来越粗糙。在下面这张图片中,我们看下不断重绘 50% 透明度的红色会出现什么状况。正如你看到的,只需三次重绘,颜色就十分接近纯色了。这就是为什么设置了 ANTI_ALIAS_FLAG 后你们图形的边缘还是十分粗糙。



我该如何解决这问题?
这里有两个选择。

避免重绘。
在重绘前清空你的 Bitmap。
下面我修改了上文的代码,添加一行代码让它在每次重绘前先清空 Bitmap。当然,如果你觉得纯色更加符合你的需求的话,你也可以不用每次都清空 Bitmap。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (bitmap == null) {
            bitmap = Bitmap.createBitmap(200,
                                         200,
                                         Bitmap.Config.ARGB_8888);
            bitmapCanvas = new Canvas(bitmap);
        }
        bitmapCanvas.drawColor(
                  Color.TRANSPARENT,
                  PorterDuff.Mode.CLEAR); //this line moved outside if
        drawOnCanvas(bitmapCanvas);
        canvas.drawBitmap(bitmap, mLeftX, mTopY, p);
    }

    protected void drawOnCanvas(Canvas canvas) {
        canvas.drawCircle(mLeftX + 100, mTopY + 100, 100, p);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
现在, Bitmap 会在每次重绘前先清空。下面的图片就是代码更改后的效果。



注意: 如果不需要经常修改 Bitmap,你可以只(在 if 条件语句中)初始化并绘制 Bitmap 一次,然后在 onDraw 方法中将其绘制到 Canvas 上,这样能保证更好的性能。也意味着频繁地清空像素并绘制圆形的操作是没有必要的。

总结
如需要先绘制到 Bitmap 上: 
你想保存图像。
你想绘制透明的像素。
你的图像不需要经常改变并且/或者需要耗时操作。
通过设置 ANTI_ALIAS_FLAG 属性绘制平滑的边缘。
避免在 Bitmap 上重绘,或者在重绘前先清空 Bitmap。
android 使path平滑
2016年08月24日 10:27:46 Ryan_田震 阅读数:878
多个点可以连成一个折线,如何将折线的拟合处变为曲线,使得整个线看上去更加平滑呢?
方法1:
Paint.setStrokeJoin(Paint.Join.ROUND)
这个方法可以将path中所有线段的Join方式设置为ROUND,实际效果就是拟合处变成了更加平滑的曲线;
方法2:
CornerPathEffect cornerPathEffect = new CornerPathEffect(200);
Paint.setPathEffect(cornerPathEffect);
此处的200就是平滑的度数;
方法3:
自己去实现,原理很简单,就是在两条线段相连的地方
原始做法:
    moveTo(x1,y1);
    lineTo(x2,y2);
    lineTo(x3,y3);
    此时就是两条线段相连;
平滑做法:
    moveTo(x1,y1);
    lineTo(x21,y21);(x21,y21)是(x1,y1)--(x2,y2)上的一个点,很接近(x2,y2);
    quadTo(x31,y31);(x31,y31)是(x2,y2)--(x3,y3)上的一个点,很接近(x2,y2);
    lineTo(x3,y3);
具体实现上还要考虑很多,例如两条线段的长度啊,形成的角度等等;
PS:推荐使用第一种,实现简单,而且不需要像方法2那样设置一个固定的角度(设置固定角度的效果不是很适用于所有角度的折线);

以上是关于canvas 绘图画出的线条粗细不一致的主要内容,如果未能解决你的问题,请参考以下文章

WPF4文字模糊不清晰边框线条粗细不一致的解决方法

WPF4文字模糊不清晰边框线条粗细不一致的解决方法

实时钟表,Easyx,C++

HTML5 canvas lineTO()方法如何在同一个画布画不同粗细的,颜色的线条出来

怎么用函数画出弧线?

h5 的canvas标签,画出的图形怎么设置他的层级关系。像css的z-index这样设置层级