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(),才可以把我们要裁剪的内容画出来
效果图:
- 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()对区域进行裁剪
这个就不举例子了,最上面的那个就是,如果有兴趣的话,可