Paint的用法总结
上一期在总结canvas的用法时候谈到绘制需要的四个组件:一个保存像素的Bitmap,一个主持绘画操作的Canvas(往Bitmap写东西),一个绘制的基本元素(例如Rect,Path,Text,Btimap),一支画笔(用来描述绘画的颜色与风格)。
在上一期文章里总结了canvas的一些用法,那么这次就来总结一下画笔Paint的一些用法。 还是一样的套路,先看看类描述:Paint是持有绘制几何图形、文字、位图等样式、颜色信息的类,那么总结下来可以大致描述,Paint是描述的绘制样式以及颜色信息的类。
Paint关于颜色信息的描述
canvas在绘制的时候颜色也不完全由paint决定,canvas在画颜色以及位图的时候,颜色画出来的跟paint的颜色不一致,因为canva在画颜色的时候,颜色信息是直接设置到参数里的,这个颜色由参数的色值来决定;在画bitmap的时候,bitmap中有相关的颜色信息,绘制出来的颜色由bitmap的颜色信息来决定;其余的绘制颜色就由paint来决定。
Paint设置颜色
Paint设置颜色有两个方法,setcolor以及setARGB跟canvas的绘制颜色一样,比较简单,另外paint还有一个setAlpha方法,这个是只设置alpha信息的
Paint设置着色器
Paint除了直接设置颜色,还可以通过setShader方法设置着色器,其实用过ps的都知道,着色器其实就是一种颜色填充方式,单一的颜色,也可以看做是纯色的着色器,如果设置了着色器,那么颜色填充方式就以着色器的颜色信息为准。使用着色器并不能直接使用Shader类,应该使用其子类:
LinearGradient线性渐变
/** * * @param x0 渐变线开始点的x坐标 * @param y0 渐变线开始点的y坐标 * @param x1 渐变线结束点的x坐标 * @param y1 渐变线结束点的y坐标 * @param color0 渐变线的起始颜色 * @param color1 渐变线的结束颜色 * @param tile 平铺的模式(需要填充的面积大于渐变的区间时的平铺模式)*/ val linearGradient = LinearGradient(0f, 0f, 200f, 200f, Color.RED, Color.BLUE, Shader.TileMode.MIRROR) mPaint.shader = linearGradient canvas?.drawText("kevinxieyeah", 5f, 200f, mPaint)复制代码
还可以指定多个颜色的线性渐变
/** * * @param x0 渐变线开始点的x坐标 * @param y0 渐变线开始点的y坐标 * @param x1 渐变线结束点的x坐标 * @param y1 渐变线结束点的y坐标 * @param colors 颜色的int数组 * @param positions 位置的float数组 * @param tile 平铺的模式(需要填充的面积大于渐变的区间时的平铺模式)*/val linearGradient = LinearGradient(0f, 0f, 200f, 200f, intArrayOf(Color.RED, Color.BLUE, Color.GREEN), floatArrayOf(0f, 0.5f, 1f), Shader.TileMode.MIRROR)复制代码
还有一个tile模式,我们指定三个值 MIRROR
CLAMP REPEATRadialGradient环形渐变
/** * * @param centerX 渐变的中心点x坐标 * @param centerY 渐变的中心点y坐标 * @param radius 渐变的半径 * @param centerColor 渐变的开始颜色 * @param edgeColor 渐变的结束颜色 * @param tileMode 填充模式 */val radialGradient = RadialGradient(300f, 300f, 500f, Color.RED, Color.GREEN, Shader.TileMode.CLAMP)mPaint.shader = radialGradientcanvas?.drawRect(Rect(0, 0, 1000, 1000), mPaint)复制代码环形渐变也可以指定多个颜色值,跟线性渐变类似,填充模式跟线性渐变也类似
SweepGradient扫描渐变
/** * * @param cx 渐变的中心点x坐标 * @param cy 渐变的中心点y坐标 * @param color0 渐变的开始颜色 * @param color1 渐变的结束颜色 */val sweepGradient = SweepGradient(300f, 300f, Color.RED, Color.GREEN)mPaint.shader = sweepGradientcanvas?.drawRect(Rect(0, 0, 1000, 1000), mPaint)复制代码扫描渐变也可以指定多个颜色值,跟线性渐变类似,但是没有填充模式
BitmapShader图片着色器
/** * @param bitmap 要填充的bitmap * @param tileX x方向的填充模式 * @param tileY y方向的填充模式 */val bitmapShader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)mPaint.shader = bitmapShadercanvas?.drawRect(Rect(0, 0, 1000, 1000), mPaint)复制代码改变一下y方向的模式看看效果 REPEAT MIRROR
ComposeShader组合着色器
val bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.a)val bitmapShader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.MIRROR)val radialGradient = RadialGradient(300f, 300f, 500f, Color.RED, Color.GREEN, Shader.TileMode.CLAMP)/** * @param shaderA 目标着色器 * @param shaderB 源着色器 * @param mode 混合模式*/val composeShader = ComposeShader(bitmapShader, radialGradient, PorterDuff.Mode.LIGHTEN)mPaint.shader = composeShadercanvas?.drawRect(Rect(0, 0, 1000, 1000), mPaint)复制代码顾名思义,把着色器混合着用,后面可以传PorterDuff.Mode和XferMode,XferMode其实就是对PorterDuff.Mode的封装,所以还是需要了解每一种混合模式之间的区别。
PorterDuff.Mode
混合模式我们对照图来达到我们需要的效果就可以了Paint设置ColorFilter
为绘制的内容提供颜色过滤,他有三个子类LightingColorFilter、PorterDuffColorFilter、ColorMatrixColorFilter,还是逐一使用看看效果先
LightingColorFilter
是不是有一种无法预测结果的感觉?其实颜色混合都有其对应的算法去处理颜色,网上看到扔物线大神这个对LightingColorFilter的解释,LightingColorFilter 的构造方法是 LightingColorFilter(int mul, int add) ,参数里的 mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加R' = R * mul.R / 0xff + add.RG' = G * mul.G / 0xff + add.GB' = B * mul.B / 0xff + add.B复制代码
PorterDuffColorFilter
val bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.a)mPaint.colorFilter = PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN)canvas?.drawBitmap(bitmap, 50f, 50f, mPaint)复制代码PorterDuffColorFilter是使用一种特定的颜色对绘制的内容按照PorterDuff.Mode来进行处理
ColorMatrixColorFilter
还是借用扔物线大神的解释,ColorMatrixColorFilter 使用一个 ColorMatrix 来对颜色进行处理。 ColorMatrix 这个类,内部是一个 4x5 的矩阵
[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]复制代码
通过计算, ColorMatrix 可以把要绘制的像素进行转换。对于颜色 [R, G, B, A] ,转换算法是这样的:
R’ = a*R + b*G + c*B + d*A + e;G’ = f*R + g*G + h*B + i*A + j;B’ = k*R + l*G + m*B + n*A + o;A’ = p*R + q*G + r*B + s*A + t;复制代码
Paint设置XferMode
setXfermode其实就是将你绘制的内容作为源图像与canvas已经存在的内容目标图像按照设置的模式进行混合,其实默认的模式是SRC_OVER,也就是像我们常见的后画的内容覆盖先画的内容。
val xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)canvas?.drawRect(Rect(300, 300, 800, 800), mPaint)mPaint.xfermode = xfermodemPaint.color = Color.REDcanvas?.drawCircle(300f, 300f, 100f, mPaint)mPaint.xfermode = null复制代码
出现了奇怪的效果,绘制的圆变成了黑色,没有达到我们预期的裁剪效果,解决方法是将设置XferMode的绘制单独放在一个图层,通过saveLayer可以避免奇怪的效果
Paint关于样式的描述
Paint的flag配置
我们在创建paint的时候可以传递一个flag值,这些值对应的设置如下:
- Paint.ANTI_ALIAS_FLAG :抗锯齿标志
- Paint.FILTER_BITMAP_FLAG : 使位图过滤的位掩码标志
- Paint.DITHER_FLAG : 使位图进行有利的抖动的位掩码标志
- Paint.UNDERLINE_TEXT_FLAG : 下划线
- Paint.STRIKE_THRU_TEXT_FLAG : 中划线
- Paint.FAKE_BOLD_TEXT_FLAG : 加粗
- Paint.LINEAR_TEXT_FLAG : 使文本平滑线性扩展的油漆标志
- Paint.SUBPIXEL_TEXT_FLAG : 使文本的亚像素定位的绘图标志
- Paint.EMBEDDED_BITMAP_TEXT_FLAG : 绘制文本时允许使用位图字体的绘图标志
我们可以通过设置flag来给paint设置这些配置,也可以用一些专门的帮助方法,比如:setAntiAlias、setDither、setLinearText、setSubpixelText、setUnderlineText、setStrikeThruText、setFakeBoldText、setFilterBitmap,这些方法都是对以上flag的帮助设置,用哪种方法都可以。
Paint的style
paint可以通过setStyle(Style style)方法来设置style,Style用三种模式,FILL(填充)、STROKE(描边)、FILL_AND_STROKE(填充以及描边)
val path = Path()path.lineTo(0f, 300f)path.lineTo(500f, 300f)path.lineTo(600f, 0f)canvas?.drawPath(path, mPaint)复制代码从上面两张图对比就可以看出填充和描边的区别,但是记住填充并不是一定要闭合的区间,因为一旦paint的style是填充,那么他会默认将path按照闭合区间来填充
Paint的描边设置
我们可以通过setStrokeWidth来设置描边的宽度,用setStrokeCap来设置线条两头的形状
mPaint.strokeCap = Paint.Cap.BUTTcanvas?.drawLine(100f, 100f, 500f, 100f, mPaint)mPaint.strokeCap = Paint.Cap.ROUNDcanvas?.drawLine(100f, 200f, 500f, 200f, mPaint)mPaint.strokeCap = Paint.Cap.SQUAREcanvas?.drawLine(100f, 300f, 500f, 300f, mPaint)复制代码
还可以通过setStrokeJoin来设置线条相交处的处理
BEVEL
ROUND MITERsetStrokeMiter是对于 setStrokeJoin() 的一个补充,它用于设置 MITER 型拐角的延长线的最大值,夹角太小时,就会造成尖角过长时,自动改用 BEVEL 的方式来渲染连接点
setPathEffect(PathEffect effect)是用来设置描边的效果,看一下有哪些子类
我们来分别设置看看效果-
DashPathEffect(floatArrayOf(10f, 10f, 20f, 20f), 0f),第一个参数 intervals 是一个数组,它指定了虚线的格式:数组中元素必须为偶数(最少是 2 个),按照(画线长度、空白长度、画线长度、空白长度)的顺序排列,第二个参数 phase 是虚线的偏移量。
-
PathDashPathEffect(path, 10f, 0f, PathDashPathEffect.Style.ROTATE)是使用指定的Path来绘制描边。
-
CornerPathEffect(30f)就是对路径进行圆角
-
DiscretePathEffect(5f, 5f)是把绘制改为使用定长的线段来拼接,并且在拼接的时候对路径进行随机偏离。segmentLength 是用来拼接的每个线段的长度, deviation 是偏离量。
-
SumPathEffect(dashPathEffect, cornerPathEffect)把两个效果直接叠加在一起
-
ComposePathEffect(dashPathEffect, cornerPathEffect)把两个效果直进行组合成一个新的效果
Paint的色彩优化
Paint 的色彩优化有两个方法: setDither(boolean dither) 和 setFilterBitmap(boolean filter) 。它们的作用都是让画面颜色变得更加自然。
setDither设置抖动,在图像降低色彩深度绘制时,避免出现大片的色带与色块
setFilterBitmap设置双线性过滤,图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。
Paint的设置阴影
mPaint.setShadowLayer(30f, 0f, 0f, Color.RED)canvas?.drawText("kevinxie", 50f, 300f, mPaint)复制代码
Paint的设置遮罩过滤
遮罩其实就是在图像上面附加一层遮盖物,可以使用setMaskFilter(MaskFilter maskfilter)来进行设置,MaskFilter有EmbossMaskFilter、BlurMaskFilter两个子类,我们来使用一下看看效果
-
BlurMaskFilter模糊效果
四种模糊的style -
EmbossMaskFilter浮雕效果,相对于之前的BlurMaskFilter来说,EmbossMaskFilter的可用性比较低,因为它实现的效果不是很霸气。正如其名,他可以实现一种类似浮雕的效果,说白了就是让你绘制的图像感觉像是从屏幕中“凸”起来更有立体感一样。
上述两种效果已经过时,在4.0以上要看到效果需要关闭硬件加速
恩,差不多,就到这里了!!!