自定义绘制 Paint高级用法

本篇为读HenCoder的总结,之后总结会持续跟进,如有错误,请指出

原文链接:http://hencoder.com/ui-1-2/

一、初始化

  1. reset()

    重置Paint的所有属性,相当于重新new一个

  2. set(Paint src)

    将一个已有的src的所有属性复制过来

  3. setFlags(int flags)

    批量设置flags,eg:

    mPaint.setFlags(Paint.ANTI_ALIAS_FLAG|Paint.DITHER_FLAG);
    复制代码

二、颜色相关

回顾一下上期内容,像素的色彩填充具体怎么做?不同的绘制内容有不同的控制方式

  • canvas.drawColor/RGB/ARGB()直接绘制颜色;
  • canvas.drawBitmap()的颜色是由bitmap的颜色来提供的
  • 图形和文字,需要Paint来设置
1.直接设置颜色
  • setColor(int color)

    paint.setColor(Color.parseColor("#009688"));  
    canvas.drawRect(30, 30, 230, 180, paint);
    
    paint.setColor(Color.parseColor("#FF9800"));  
    canvas.drawLine(300, 30, 450, 180, paint);
    
    paint.setColor(Color.parseColor("#E91E63"));  
    canvas.drawText("HenCoder", 500, 130, paint);  
    复制代码
  • setARGB

    paint.setARGB(100, 255, 0, 0);  
    canvas.drawRect(0, 0, 200, 200, paint);  
    paint.setARGB(100, 0, 0, 0);  
    canvas.drawLine(0, 0, 200, 200, paint);  
    复制代码
2.setShader(Shader shader)
  • 设置shader之后setColor/ARGB不管用

  • 首先了解一下着色规则TileMode

    TileMode 一共有 3 个值可选: CLAMP, MIRRORREPEATCLAMP 会在端点之外延续端点处的颜色;MIRROR是镜像模式;REPEAT 是重复模式。

Shader分类

  • 颜色渐变着色器

    • LinearGradient 线性渐变着色器

      构造方法 LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

      x0 y0 x1 y1:渐变的两个端点的位置 color0 color1 是端点的颜色 tile:端点范围之外的着色规则

    Shader shader = new LinearGradient(100, 100, 500, 500, Color.parseColor("#E91E63"),  
            Color.parseColor("#2196F3"), Shader.TileMode.CLAMP);
    paint.setShader(shader);
    
    ...
    
    canvas.drawCircle(300, 300, 200, paint);  
    复制代码

    示例图

    • RadialGradient 辐射渐变着色器

      构造方法RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)

      centerX centerY:辐射中心的坐标 radius:辐射半径 centerColor:辐射中心的颜色 edgeColor:辐射边缘的颜色 tileMode:辐射范围之外的着色模式。

      Shader shader = new RadialGradient(300, 300, 200, Color.parseColor("#E91E63"),  
              Color.parseColor("#2196F3"), Shader.TileMode.CLAMP);
      paint.setShader(shader);
      
      ...
      
      canvas.drawCircle(300, 300, 200, paint);  
      复制代码

      示例图

    • SweepGradient 扫描渐变着色器

      构造方法 SweepGradient(float cx, float cy, int color0, int color1)

      cx cy :扫描的中心 color0:扫描的起始颜色 color1:扫描的终止颜色

      Shader shader = new SweepGradient(300, 300, Color.parseColor("#E91E63"),  
              Color.parseColor("#2196F3"));
      paint.setShader(shader);
      
      ...
      
      canvas.drawCircle(300, 300, 200, paint);  
      复制代码

      示例图

  • BitmapShader 图片着色器

    Bitmap 的像素来作为图形或文字的填充

    构造方法 BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

    bitmap:用来做模板的 Bitmap 对象 tileX:横向的 TileMode tileY:纵向的 TileMode

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
    Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);  
    paint.setShader(shader);
    
    ...
    
    canvas.drawCircle(300, 300, 200, paint);  
    复制代码

    示例图

  • ComposeShader 混合着色器

    把两个 Shader 一起使用

    构造方法 ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

    shaderA, shaderB:两个相继使用的 Shader mode: 两个 Shader 的叠加模式,即 shaderAshaderB 应该怎样共同绘制。它的类型是 PorterDuff.Mode

    // 第一个 Shader:头像的 Bitmap
    Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
    Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
    // 第二个 Shader:从上到下的线性渐变(由透明到黑色)
    Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo);  
    Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
    // ComposeShader:结合两个 Shader
    Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER);  
    paint.setShader(shader);
    
    ...
    
    canvas.drawCircle(300, 300, 300, paint);  
    复制代码

    示例图

PorterDuff.Mode

指定两个图像共同绘制时的策略。官方说明

总共17中,分为两类

  • Alpha合成(Alpha Compositing
    • 混合(Blending

Alpha合成比较简单直观,也是需要掌握的,混合模式只需记住就ok。

图解

第一类:Alpha合成

第二类:混合模式Blending

3.setColorFilter(ColorFilter colorfilter)

Paint除了setColor/ARGB,setShader设置颜色外,还可以setColorFilter,对颜色进行过滤

  • LightingColorFilter 模拟简单的光照效果

    构造方法 LightingColorFilter(int mul, int add)

    参数里的 muladd 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加

    公式

    R' = R * mul.R / 0xff + add.R  
    G' = G * mul.G / 0xff + add.G  
    B' = B * mul.B / 0xff + add.B  
    复制代码

    示例

    //去掉红色  见图左
    ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000);  
    paint.setColorFilter(lightingColorFilter);  
    //对应公式
    R' = R * 0x0 / 0xff + 0x0 = 0 // 红色被移除  
    G' = G * 0xff / 0xff + 0x0 = G  
    B' = B * 0xff / 0xff + 0x0 = B  
    
    //增强绿色 见图右
    ColorFilter lightingColorFilter = new LightingColorFilter(0xffffff, 0x003000);  
    paint.setColorFilter(lightingColorFilter);  
    //对应公式
    R' = R * 0xff / 0xff + 0x0 = R  
    G' = G * 0xff / 0xff + 0x30 = G + 0x30 // 绿色被加强  
    B' = B * 0xff / 0xff + 0x0 = B  
    复制代码

  • PorterDuffColorFilter

    使用一个指定的颜色和一种指定PorterDuff.Mode与绘制对象进行合成。

    构造:PorterDuffColorFilter(int color, PorterDuff.Mode mode)

    color 参数是指定的颜色, mode 参数是指定的 Mode

  • ColorMatrixColorFilter

    使用一个ColorMatrix来对颜色进行处理,ColorMatrix是一个4x5的矩阵

    [ a, b, c, d, e,
      f, g, h, i, j,
      k, l, m, n, o,
      p, q, r, s, t ]
    复制代码

    转换算法

    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;  
    复制代码

    ColorMatrix 有一些自带的方法可以做简单的转换

    使用 setSaturation(float sat) 来设置饱和度

    也可以自己去设置它的每一个元素来对转换效果做精细调整

    参考:StyleImageView

4.setXfermode(Xfermode mode)

Xfermode是指你要绘制的内容与Canvas的目标位置的内容应该怎样结合计算出最终的颜色。简单说,以需要绘制的n内容作为源图像,以View中已有的内容作为目标图像,选取一种PorterDuff.Mode作为绘制内容的颜色处理方案

Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

...

canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方  
paint.setXfermode(xfermode); // 设置 Xfermode  
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆  
paint.setXfermode(null); // 用完及时清除 Xfermode  
复制代码

注意事项
  • 使用离屏缓冲 (Off-screen buffer

    不设置离屏缓冲

    设置离屏缓冲

    怎么设置

    • canvas.saveLayer()

      saveLayer() 可以做短时的离屏缓冲。使用方法很简单,在绘制代码的前后各加一行代码,在绘制之前保存,绘制之后恢复:

      int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
      复制代码

      canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方 paint.setXfermode(xfermode); // 设置 Xfermode canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆 paint.setXfermode(null); // 用完及时清除 Xfermode

      canvas.restoreToCount(saved); ​```

    • view.setLayerType()

      View.setLayerType() 是直接把整个 View 都绘制在离屏缓冲中。 setLayerType(LAYER_TYPE_HARDWARE) 是使用 GPU 来缓冲, setLayerType(LAYER_TYPE_SOFTWARE) 是直接直接用一个 Bitmap 来缓冲。

  • 控制好透明区域

    让透明区域足够覆盖到要和它结合绘制的内容,为什么 请看图

三、效果相关

  • setAntiAlias(boolean aa) 设置抗锯齿

  • setStyle(Paint.Style style) 设置填充风格

    • Paint.Style.FILL 填充 默认
    • Paint.Style.STROKE 描边
    • Paint.Style.FILL_AND_STROKE 填充并描边
  • 线条形状

    • setStrokeWidth(int width) 设置线条宽度

    • setStrokeCap(Paint.Cap cap) 设置线头的形状

      • BUTT 平头 默认
      • ROUND 圆头
      • SQUARE 方头

    • setStrokeJoin(Paint.Join join) 设置拐角形状

      • MITER 尖角 默认
      • BEVEL 平角
      • ROUND 圆角

    • setStrokeMiter(float miter)setStrokeJoin的补充,设置MITER形状拐角的最大值

  • 色彩优化

    • setDither(boolean dither) 设置图像的抖动

      把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。

      paint.setDither(true);  
      复制代码

    • setFilterBitmap(boolean filter) 设置双线性过滤

      图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑

      paint.setFilterBitmap(true);  
      复制代码

  • setPathEffect(PathEffect pathEffect) 给图形轮廓设置效果

    注意: PathEffect 在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:

    1. Canvas.drawLine()Canvas.drawLines() 方法画直线时,setPathEffect() 是不支持硬件加速的;
    2. PathDashPathEffect 对硬件加速的支持也有问题,所以当使用 PathDashPathEffect 的时候,最好也把硬件加速关了。
    • CornerPathEffect 将所有拐角变成圆角

      PathEffect pathEffect = new CornerPathEffect(20);  
      paint.setPathEffect(pathEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

    • DiscretePathEffect 将线条进行随机偏离,让轮廓变得乱七八糟

      //构造方法 DiscretePathEffect(float segmentLength, float deviation)
      //segmentLength 是用来拼接的每个线段的长度, deviation 是偏离量。
      PathEffect pathEffect = new DiscretePathEffect(20, 5);  
      paint.setPathEffect(pathEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

    • DashPathEffect 使用虚线来绘制线条

      PathEffect pathEffect = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);  
      paint.setPathEffect(pathEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

    • PathDashPathEffect 使用Path绘制虚线

      Path dashPath = ...; // 使用一个三角形来做 dash  
      PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0,  
              PathDashPathEffectStyle.TRANSLATE);
      paint.setPathEffect(pathEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

    • SumPathEffect 同时作用两种PathEffect

      PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
      PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
      pathEffect = new SumPathEffect(dashEffect, discreteEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

    • ComposePathEffect 分别作用两种PathEffect

      PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
      PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
      pathEffect = new ComposePathEffect(dashEffect, discreteEffect);
      
      ...
      
      canvas.drawPath(path, paint);  
      复制代码

  • setShadowLayer(float radius, float dx, float dy, int shadowColor) 在之后的绘制内容下面加一层阴影。

    clearShadowLayer() 清楚阴影

    硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。

    如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。

    paint.setShadowLayer(10, 0, 0, Color.RED);
    
    ...
    
    canvas.drawText(text, 80, 300, paint);  
    复制代码

  • setMaskFilter(MaskFilter maskFilter) 设置在之后的绘制内容上方的附加效果。

    • BlurMaskFilter 模糊效果的 MaskFilter

      paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
      
      ...
      
      canvas.drawBitmap(bitmap, 100, 100, paint);  
      复制代码

      模糊的类型。一共有四种:

      • NORMAL: 内外都模糊绘制
      • SOLID: 内部正常绘制,外部模糊
      • INNER: 内部模糊,外部不绘制
      • OUTER: 内部不绘制,外部模糊

    • EmbossMaskFilter 浮雕效果

      paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));
      
      ...
      
      canvas.drawBitmap(bitmap, 100, 100, paint);  
      复制代码

  • 获取绘制的Path

    • getFillPath(Path src, Path dst)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值