当前位置:   article > 正文

Canvas详解(android自定义view,onDraw()绘制各种图形)

Canvas详解(android自定义view,onDraw()绘制各种图形)

Canvas

Canvas:The Canvas class holds the “draw” calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

根据doc的介绍我们可以看到canvas是画集合图形的一个重要类,它持有draw动作(就是画图).使用draw的时候我们需要4个基本条件:
1. 一个用来保存像素的Bitmap
2. Canvas用来调用draw方法
3. 我们要画的材料(Rect,Path,Text,Bitmap,Color…)
4. Paint,用来设置内容的色值,样式,透明度…
这是根据文档中的介绍而翻译的,但是我们可以仔细瞅瞅,平时我们的自定义view也就是这个条件,例子我就列举了,随便找个自定义view就可以发现都是这么做的.

函数和常量的介绍

常量
  • ALL_SAVE_FLAG
    在调用saveLayer()时调用,用来表示保存全部数据,例如色值和透明度等等…,在调用restore()可以恢复全部数据.
  • MATRIX_SAVE_FLAG
    只保存图层的matrix矩阵(开启硬件加速),在O版本中,canvas自动含有该功能
  • CLIP_SAVE_FLAG
    使用方法同上:只是保存和恢复的是当前clip的内容(开启硬件加速),在O版本中,canvas自动含有该功能
  • CLIP_TO_LAYER_SAVE_FLAG
    创建图层时,会把canvas(所有图层)裁剪到参数指定的范围,如果省略这个flag将导致图层开销巨大(实际上图层没有裁剪,与原图层一样大)
  • FULL_COLOR_LAYER_SAVE_FLAG
    完全保留该图层颜色(和上一图层合并时,清空上一图层的重叠区域,保留该图层的颜色)
  • HAS_ALPHA_LAYER_SAVE_FLAG
  • 表明该图层有透明度,和下面的标识冲突,都设置时以下面的标志为准

MATRIX_SAVE_FLAG,CLIP_SAVE_FLAG在使用save(flag)时被调用
ALL_SAVE_FLAG,CLIP_TO_LAYER_SAVE_FLAG,FULL_COLOR_LAYER_SAVE_FLAG,HAS_ALPHA_LAYER_SAVE_FLAG在使用saveLayer()或者saveLayerAlpha时调用.

我们可以使用如下code来进行测试:

...
canvas?.save(Canvas.flag);
...
canvas?.draw()...
canvas?.restore();
...
canvas?.draw()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
构造函数
  • Canvas()
  • Canvas(Bitmap bitmap) bitmap要绘制的位图
函数
  • setBitmap(Bitmap bitmap)bitmap是可变的
    使用该函数后,除了当前矩阵和剪辑堆栈(例如save(Matrix,Clip))之外,所有画布状态如层、过滤器和保存/恢复堆栈(saveLayer()…)都将重置

Canvas(Bitmap bitmap) == Canvas() + setBitmap(Bitmap bitmap)

裁剪:clipPath();clipRect()
在介绍裁剪前,我们先介绍一下region这个类.

Region

用于指定的几何区域剪裁区域图。

Region.Op

组合两个区域时可以执行的逻辑操作,既在多个裁剪区域发生重叠时,可以使用该类来实现我们需要的功能,如果是单个图形裁剪时,各个值对应显示的裁剪形状相同,同时,clip()都有默认值

我们可以举例:有A和B两个几何图形,对他们进行裁剪,同时他们相交,B使用Region.Op指定

  • DIFFERENCE(0),从第一个区域减去op区域(是A形状中不同于B的部分显示出来)
  • INTERSECT(1),显示相交的两个地区
  • UNION(2),显示这两个地区结合起来全部显示
  • XOR(3),显示全集部分减去相交部分
  • REVERSE_DIFFERENCE(4),是B形状中不同于A的部分显示出来(既显示Op不相交的部分)
  • REPLACE(5);用op区域替换dst区域(既只显示op区域)

注:该部分的含义我们可以从SkRegion.h中的注释了解,如果想要理解具体内容的话,可以自己研究SkRegion.h,SkRegion.cpp

    /**
     *  The logical operations that can be performed when combining two regions.
     */
    enum Op {
        kDifference_Op, //!< subtract the op region from the first region
        kIntersect_Op,  //!< intersect the two regions
        kUnion_Op,      //!< union (inclusive-or) the two regions
        kXOR_Op,        //!< exclusive-or the two regions
        /** subtract the first region from the op region */
        kReverseDifference_Op,
        kReplace_Op,    //!< replace the dst region with the op region

        kLastOp = kReplace_Op
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:op指的是我们裁剪时,裁剪完后显示的图形区域.如果clip()中没有使用Region.Op时,我们可以去Canvas中看看,一般都有默认Region.Op被调用
示例:

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    canvas?.save()
    canvas?.translate(5f, 5f)
    paint.setColor(Color.parseColor("#00ff00"))
    canvas?.drawRect(RectF(0f, 0f, 300f, 300f), paint)
    canvas?.drawCircle(300f, 300f, 150f, paint)

    paint.setColor(Color.parseColor("#ff0000"))

    // 对一个矩形进行裁剪
    canvas?.clipRect(RectF(0f, 0f, 300f, 300f))

    val mPath = Path()
    mPath.addCircle(300f, 300f, 150f, Path.Direction.CCW)
    // 对指定的path进行裁剪
    canvas?.clipPath(mPath, Region.Op.INTERSECT)

    // 显示裁剪内容
    canvas?.drawRect(RectF(0f, 0f, Integer.MAX_VALUE.toFloat(), Integer.MAX_VALUE.toFloat()), paint)
    canvas?.restore()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

我们可以看到Rect裁剪和Path裁剪完后显示出来的内容
注意:使用完clip()后,必须使用canvas.draw(),才可以把我们要裁剪的内容画出来

效果图:
使用Region.Op剪切的效果图

  • clipPath()对路径进行裁剪
    • clipPath(Path path, Region.Op op) // 指定裁剪路径和显示的内容
    • clipPath(Path path) // 指定裁剪路径
      使用clipPath可以根据path将图片裁剪成各种形状:
      例如:圆形图片
override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    canvas?.save()
    canvas?.translate(5f, 5f)
    var width: Int = bitmap.width
    var hegith: Int = bitmap.height
    var radius: Int
    when {
        width < hegith -> radius = width / 2
        width > hegith -> radius = hegith / 2
        else -> radius = width / 2
    }
    var path = Path()
    // path.addCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), Path.Direction.CW)
    path.addRoundRect(RectF(0f, 0f, width.toFloat(), hegith.toFloat()), 30f, 30f, Path.Direction.CW)
    canvas?.clipPath(path, Region.Op.INTERSECT)
    canvas?.drawBitmap(bitmap, 0f, 0f, paint)
    canvas?.restore()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

圆形头像
矩形弧度头像
我记得前边写过这种图形,其中一个是BitmapShader,如果有兴趣的话可以去看看这个类,这个类可以实现图片的各种形状.
canvas.clipPath()也可以根据Path绘制各种形状的图片,例如五角星,三角形,正方形…….

  • clipRect()使用矩形进行裁剪
    • clipRect(Rect rect) // 按指定矩形裁剪
    • clipRect(Rect rect,Region.Op op) 按指定矩形裁剪,并指定显示内容
    • ……其他clipRect()和上边的这两个基本没有什么区别.只是Rect显示方式不同而已
paint.setShader(BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
canvas?.drawRect(0f, 0f, 600f, 300f, paint)
paint.setShader(null)
paint.color = Color.parseColor("#4400ff00")
canvas?.clipRect(50, 20, 550, 280)
canvas?.drawRect(0f, 0f, Float.MAX_VALUE, Float.MAX_VALUE, paint)
canvas?.restore()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里写图片描述

paint.setShader(BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP))
paint.color = Color.RED
canvas?.clipRect(50, 20, 400, 200)
canvas?.drawRect(0f, 0f, 600f, 300f, paint)
paint.setShader(null)
  • 1
  • 2
  • 3
  • 4
  • 5

这里写图片描述

  • clipRegion()对区域进行裁剪

这个就不举例子了,最上面的那个就是,如果有兴趣的话,可以自己找些例子看看,或者自己写写

  • clipOut() 对指定形状进行剪切,显示差值部分
    • clipOutRect()
    • clipOutPath()

这个也不举例了,把上面的代码复制一份,将clip()修改为clipOut()就可以了,注意这个clipOut()需要运行在API26版本上.



动画:移动(translate),旋转(rotate),缩放(scale),错切(skew);矩阵(Matrix)变换(translate,rotate,scale,skew)

canvas的几何变换实际上还是对画布的矩阵进行改变来实现目地的

例:

/**
 * Preconcat the current matrix with the specified translation
 *
 * @param dx The distance to translate in X
 * @param dy The distance to translate in Y
 */
public 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/765453
推荐阅读
相关标签
  

闽ICP备14008679号