2效果
效果类的API,指的就是抗锯齿,填充/轮廓,线条宽度等。
2.1.setAntiAlias(Boolean true) 设置抗锯齿
抗锯齿默认关闭,如果需要抗锯齿,需要手动打开。可以在构造方法中传入Paint.ANIT_ALIAS_FLAG或者通过setAntiAlias(true)来打开
2.2.setStyle(Paint.Style Style)
Paint.Style 一共有三种模式:
2.3.线条形状
设置线条形状的方法共有4个:
setStrokeWidth(float width),
setStrokeCap(Paint.Cap cap),
setStrokeJoin(Paint.Join join)
setStrokeMiter(float miter)
2.3.1setStrokeWidth(float width)
设置线条宽度,默认为0。
扩:
默认情况下,线条宽度为 0,但你会发现,这个时候它依然能够画出线,线条的宽度为 1 像素。那么它和线条宽度为 1 有什么区别:
你可以为 Canvas 设置 Matrix 来实现几何变换(如放大、缩小、平移、旋转),在几何变换之后 Canvas 绘制的内容就会发生相应变化,包括线条也会加粗,例如 2 像素宽度的线条在 Canvas 放大 2 倍后会被以 4 像素宽度来绘制。而当线条宽度被设置为 0 时,它的宽度就被固定为 1 像素,就算 Canvas 通过几何变换被放大,它也依然会被以 1 像素宽度来绘制。Google 在文档中把线条宽度为 0 时称作「hairline mode(发际线模式)」。
2.3.2setStrokeCap(Paint.Cap cap)
设置线头的形状。共有三种类型:BUTT平头,ROUND圆头,SQUARE方头,默认为BUTT平头。
注:如果线条的宽度为1像素的时候,这三种状态是一样的。
2.3.3setStrokeJoin(Paint.Join join)
设置拐角的形状。有三种状态:MITER尖角,BEVEL 平角,ROUND圆角。
2.3.4setStrokeMiter(float miter)
这个是对于setStrokeJoin(float miter)的补充,他用于设置MITER拐角的延长线的最大值。
例外:如果拐角的角度太小,有可能会出现连接点过长的情况。
MITER 型连接点有一个额外的规则:当尖角过长时,自动改用 BEVEL 的方式来渲染连接点。
2.4 色彩优化
Paint 的色彩优化有两个方法: setDither(boolean dither) 和 setFilterBitmap(boolean filter) 。它们的作用都是让画面颜色变得更加顺眼。
2.4.1 setDither(boolean dither)
设置图像抖动
所谓抖动(注意,它就叫抖动,不是防抖动,也不是去抖动,有些人在翻译的时候自作主张地加了一个「防」字或者「去」字,这是不对的),是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。
比如向 1 位色彩深度的区域中绘制灰色,由于 1 位深度只包含黑和白两种颜色,在默认情况下,即不加抖动的时候,只能选择向上或向下选择最接近灰色的白色或黑色来绘制,那么显示出来也只能是一片白或者一片黑。而加了抖动后,就可以绘制出让肉眼识别为灰色的效果了:
不过,抖动可不只可以用在纯色的绘制。在实际的应用场景中,抖动更多的作用是在图像降低色彩深度绘制时,避免出现大片的色带与色块。类似于:
在android中使用
PathEffect pathEffect = new DiscretePathEffect(20, 5);
paint.setPathEffect(pathEffect);
...
canvas.drawPath(path, paint);
setDither(dither) 已经没有当年那么实用了,因为现在的 Android 版本的绘制,默认的色彩深度已经是 32 位的 ARGB_8888 ,效果已经足够清晰了。只有当你向自建的 Bitmap 中绘制,并且选择 16 位色的 ARGB_4444 或者 RGB_565 的时候,开启它才会有比较明显的效果。
2.4.2 setDilterBitmap(boolean filter)
设置双线性过滤在绘制Bitmap
图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。
在Android中使用
paint.setFilterBitmap(true);
2.5 setPathEffect(PathEffect effect)
使用 PathEffect 来给图形的轮廓设置效果。对 Canvas 所有的图形绘制有效。
Android 中的 6 种 PathEffect。PathEffect 分为两类,单一效果的 CornerPathEffect DiscretePathEffect DashPathEffect PathDashPathEffect ,和组合效果的 SumPathEffect ComposePathEffect。
2.5.1CornerPathEffect
将所有拐角变成圆角
PathEffect pathEffect = new CornerPathEffect(20);
paint.setPathEffect(pathEffect);
2.5.2DiscretePathEffect
将线条进行随机偏离,让轮廓乱七八糟。乱七八糟的程度由参数决定。
PathEffect pathEffect = new DiscretePathEffect(20, 5);
paint.setPathEffect(pathEffect);
DiscretePathEffect 具体的做法是,把绘制改为使用定长的线段来拼接,并且在拼接的时候对路径进行随机偏离。它的构造方法 DiscretePathEffect(float segmentLength, float deviation) 的两个参数中, segmentLength 是用来拼接的每个线段的长度, deviation 是偏离量。这两个值设置得不一样,显示效果也会不一样。
2.5.3DashPathEffect
使用虚线来绘制线条
PathEffect pathEffect = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
paint.setPathEffect(pathEffect);
它的构造方法 DashPathEffect(float[] intervals, float phase) 中, 第一个参数 intervals 是一个数组,它指定了虚线的格式:数组中元素必须为偶数(最少是 2 个),按照「画线长度、空白长度、画线长度、空白长度」……的顺序排列,例如上面代码中的 20, 5, 10, 5 就表示虚线是按照「画 20 像素、空 5 像素、画 10 像素、空 5 像素」的模式来绘制;第二个参数 phase 是虚线的偏移量。
2.5.4PathDashPathEffect
使用一个Paht来绘制虚线
Path dashPath = ...; // 使用一个三角形来做 dash
PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0,
PathDashPathEffectStyle.TRANSLATE);
paint.setPathEffect(pathEffect);
构造方法 PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style) 中, shape 参数是用来绘制的 Path ; advance 是两个相邻的 shape 段之间的间隔,不过注意,这个间隔是两个 shape 段的起点的间隔,而不是前一个的终点和后一个的起点的距离; phase 和 DashPathEffect 中一样,是虚线的偏移;最后一个参数 style,是用来指定拐弯改变的时候 shape 的转换方式。style 的类型为 PathDashPathEffect.Style ,是一个 enum ,具体有三个值:
TRANSLATE:位移
ROTATE:旋转
MORPH:变体
2.5.5SumPathEffect
这是一个组合效果类的PathEffect,就是分别按照两种PathEffect分别对目标进行绘制。
PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
pathEffect = new SumPathEffect(dashEffect, discreteEffect);
2.5.6ComposePathEffect
它是先对目标 Path 使用一个 PathEffect,然后再对这个改变后的 Path 使用另一个 PathEffect。
PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
pathEffect = new ComposePathEffect(dashEffect, discreteEffect);
构造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的两个 PathEffect 参数, innerpe 是先应用的, outerpe 是后应用的。所以上面的代码就是「先偏离,再变虚线」。而如果把两个参数调换,就成了「先变虚线,再偏离」。
注:
PathEffect 在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:
Canvas.drawLine() 和 Canvas.drawLines() 方法画直线时,setPathEffect() 是不支持硬件加速的;
PathDashPathEffect 对硬件加速的支持也有问题,所以当使用 PathDashPathEffect 的时候,最好也把硬件加速关了。
2.6 setShadowLayer(float radius, float dx, float dy, int shadowColor)
在绘制内容的下面加一层阴影
paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));
...
canvas.drawBitmap(bitmap, 100, 100, paint);
清除阴影:clearShadowLayer();
注:
在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。
如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。
2.7 setMaskFilter(MaskFilter maskfilter)
与setShadowLayer()方法相反,setShadowLayer()是在绘制曾下方附加效果。setMaskFilter()是在绘制层上方附加效果。
MaskFilter 共有两种:BluerMaskFilter EmbossMaskFilter
2.7.1 BlurMaskFilter 模糊效果
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
它的构造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 参数是模糊的范围, style 是模糊的类型。一共有四种:
NORMAL: 内外都模糊绘制
SOLID: 内部正常绘制,外部模糊
INNER: 内部模糊,外部不绘制
OUTER: 内部不绘制,外部模糊
2.7.2 EmbossMaskFilter 浮雕效果
paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));
构造方法 EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的参数里, direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。
2.8 获取绘制的Path
根据Path的位置,计算出绘制Path或文字时的实际Path
2.8.1 getFillPath(Path src,Path dst)
所谓实际 Path ,指的就是 drawPath() 的绘制内容的轮廓,要算上线条宽度和设置的 PathEffect。
默认情况下(线条宽度为 0、没有 PathEffect),原 Path 和实际 Path 是一样的;而在线条宽度不为 0 (并且模式为 STROKE 模式或 FLL_AND_STROKE ),或者设置了 PathEffect 的时候,实际 Path 就和原 Path 不一样了:
通过 getFillPath(src, dst) 方法就能获取这个实际 Path。方法的参数里,src 是原 Path ,而 dst 就是实际 Path 的保存位置。 getFillPath(src, dst) 会计算出实际 Path,然后把结果保存在 dst 里。
2.8.2 getTextPath(String text, int start, int end, float x, float y, Path path) / getTextPath(char[] text, int index, int count, float x, float y, Path path)
文字的绘制,虽然是使用 Canvas.drawText() 方法,但其实在下层,文字信息全是被转化成图形,对图形进行绘制的。 getTextPath() 方法,获取的就是目标文字所对应的 Path 。