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()
构造函数
  • 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
    };

注意: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()
}

我们可以看到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()
}

圆形头像
矩形弧度头像
我记得前边写过这种图形,其中一个是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()

这里写图片描述

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)

这里写图片描述

  • clipRegion()对区域进行裁剪

这个就不举例子了,最上面的那个就是,如果有兴趣的话,可

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值