版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
Canvas 我们经常把它理解为画板,但是,实际上 Canvas 是一个封装类,它包含了四个东西:
1、画板:用来保存像素的bitmap
2、画布或者画纸: Canvas 在 Bitmap 上进行绘制操作 —- (Layer—saveLayer操作时,新建一个透明的画布图层)
3、绘制的东西
4、绘制的画笔 Paint
drawLine(float startX, float startY, float stopX, float stopY, Paint paint) startX: 起始点 x 坐标 startY :起始点 y坐标 stopX: 结束点 x 坐标 stopY :结束点 y坐标 paint :画笔
drawLines(float[] pts, Paint paint) pts : 直线的端点数组,每 4 个点为一条直线
drawLines(float[] pts, int offset, int count, Paint paint) pts : 直线的端点数组,每 4 个点为一条直线 offset : 跳过的数据个数,跳过的数据不参与直线绘制 count : 实际参与绘制的数据个数
drawPoint(float x, float y, Paint paint) x : 点的 x 坐标 y : 点的 y 坐标
drawPoints(float[] pts, Paint paint) pts : 点的坐标数组,每 2个点为一个点
drawPoints(float[] pts, int offset, int count, Paint paint) pts : 点的坐标数组,每 2个点为一个点 offset : 跳过的数据个数,跳过的数据不参与点绘制 count : 实际参与绘制的数据个数
drawRect(RectF rect, Paint paint) rect : 矩形区域
drawRect(Rect r, Paint paint) r : 矩形区域
drawRect(float left, float top, float right, float bottom, Paint paint) left : 左边的 x 坐标 top :上边的 y 坐标 right : 右边的 x 坐标 bottom : 底边的 y 坐标
注:RectF 和 Rect 主要区别是精度,RectF 使用的是 float, Rect 使用的是 int。
drawRoundRect(RectF rect, float rx, float ry, Paint paint) rect : 矩形区域 rx:x方向上的圆角半径。 ry:y方向上的圆角半径。
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) left : 左边的 x 坐标 top :上边的 y 坐标 right : 右边的 x 坐标 bottom : 底边的 y 坐标 rx:x方向上的圆角半径。 ry:y方向上的圆角半径。
drawCircle(float cx, float cy, float radius, Paint paint) cx : 圆心的x坐标 cy : 圆心的y坐标 radius : 圆的半径
drawOval(RectF oval, Paint paint) oval : 外切矩形区域
drawOval(float left, float top, float right, float bottom, Paint paint) left : 外切矩形左边的 x 坐标 top :外切矩形上边的 y 坐标 right : 外切矩形右边的 x 坐标 bottom :外切矩形 底边的 y 坐标
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) oval : 圆弧所在椭圆外切矩形 startAngle : 圆弧的起始角度(0 为 x轴正方向) sweepAngle : 圆弧的角度 useCenter:是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不显示
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) left : 圆弧所在外切矩形左边的 x 坐标 top :圆弧所在外切矩形上边的 y 坐标 right : 圆弧所在外切矩形右边的 x 坐标 bottom :圆弧所在外切矩形 底边的 y 坐标 startAngle : 圆弧的起始角度(0 为 x轴正方向) sweepAngle : 圆弧的角度 useCenter:是否显示半径连线,true表示显示圆弧与圆心的半径连线,false表示不显示
把 useCenter 改为 false:
drawPath(Path path, Paint paint) path : 路径
path 部分不在这边细讲。
Canvas 除了画常用的形状之外,还能画 Region ,它表示的 Canvas 图层上的一块封闭的区域。由于区域是不规则的,所以在绘制的时候会被切割成多个矩形,通过迭代进行绘制。
Paint paint = new Paint(); paint.setColor(Color.RED); //样式必须设为 STROKE,否则看不出效果 paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(2); RectF r = new RectF(100, 100, 600, 800); Path path = new Path(); path.addOval(r, Path.Direction.CCW); //创建一块矩形的区域 Region region = new Region(100, 100, 600, 800); Region region1 = new Region(); //path的椭圆区域和矩形区域进行交集 region1.setPath(path, region); //结合区域迭代器使用(得到图形里面的所有的矩形区域) RegionIterator iterator = new RegionIterator(region1); Rect rect = new Rect(); //通过迭代,进行区域的绘制 while (iterator.next(rect)) { canvas.drawRect(rect, paint); }区域叠加规则枚举类 Op 的源码:
public enum Op { DIFFERENCE(0), INTERSECT(1), UNION(2), XOR(3), REVERSE_DIFFERENCE(4), REPLACE(5); Op(int nativeInt) { this.nativeInt = nativeInt; } /** * @hide */ public final int nativeInt; }效果图中 A 为调用者, B 为叠加上来的区域。
op(Rect r, Op op) r: 叠加上去的矩形 op : 叠加规则
op(int left, int top, int right, int bottom, Op op) left : 叠加上去的矩形左边的 x 坐标 top :叠加上去的矩形上边的 y 坐标 right : 叠加上去的矩形右边的 x 坐标 bottom :叠加上去的矩形 底边的 y 坐标 op : 叠加规则
op(Region region, Op op) region: 叠加上去的区域 op : 叠加规则
上方代码先绘制一个矩形,然后进行平移操作,后再次绘制一个矩形。在 translate(50, 50) 里平移的是 Canvas 的绘图坐标系,而不是 Canvas。我们可以稍微证明一下。
Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); RectF r = new RectF(0, 0, 400, 500); paint.setColor(Color.GREEN); canvas.drawRect(r, paint); //平移 canvas.translate(50, 50); canvas.drawColor(Color.YELLOW); paint.setColor(Color.BLUE); canvas.drawRect(r, paint);在平移后给画布填充颜色,发现整个屏幕都充满,说明平移的不是画布。
Canvas里面牵扯两种坐标系:Canvas 自己的坐标系、绘图坐标系。
它就在View的左上角,做坐标原点往右是X轴正半轴,往下是Y轴的正半轴,有且只有一个,唯一不变。
它不是唯一不变的,它与Canvas的Matrix有关系,当Matrix发生改变的时候,绘图坐标系对应的进行> > 改变。
用画直线的方法把平移前后的坐标系画出来。可以发现平移之后坐标系变了。
RectF r = new RectF(0, 0, 400, 500); paint.setColor(Color.GREEN); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴 canvas.translate(50, 50); paint.setColor(Color.BLUE); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴继续进行旋转45度。
RectF r = new RectF(0, 0, 400, 500); paint.setColor(Color.GREEN); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴 canvas.translate(50, 50); paint.setColor(Color.BLUE); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴 canvas.rotate(45); paint.setColor(Color.YELLOW); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴结论:绘制坐标的变化跟矩阵 Matrix 有关。Matrix又是通过我们设置translate、rotate、scale、skew来进行改变的
Canvas 绘制坐标系的变化过程是不可逆的,只能通过 save 和 restore 方法来保存和还原变化操作
RectF r = new RectF(0, 0, 400, 500); paint.setColor(Color.GREEN); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴 canvas.save(); canvas.translate(50, 50); paint.setColor(Color.BLUE); canvas.drawRect(r, paint); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴 canvas.restore(); canvas.rotate(45); paint.setColor(Color.YELLOW); canvas.drawLine(0,0,canvas.getWidth(),0,paint);// X 轴 canvas.drawLine(0,0,0,canvas.getHeight(),paint);// Y 轴在第一次变化坐标前 save,绘制坐标系后 restore, 再次进行坐标变化是从最开始的坐标系进行变化。说明第一次变化后,坐标系重置还原了。
1.只是保存和还原变换操作 Matrix 以及 Clip 剪裁, 2.可以通过 restoretoCount 直接还原到对应栈的保存状态。 3.不是真正的图层关系
1.真正的图层关系,saveLayer 的时候都会新建一个透明的图层(离屏 Bitmap-离屏缓冲),并且会将saveLayer 之前的一些 Canvas 操作延续过来 2. 后续的绘图操作都在新建的layer上面进行 3.当我们调用restore 或者 restoreToCount 时会更新到对应的图层和画布上
saveLayer 可以实现所有 save 的实现效果。save 只是在栈中保存 Matrix 以及 Clip 剪裁的变换记录,当开始绘制的时候,根据栈中保存的变换记录得出最终的绘制坐标,从而进行绘制。saveLayer 是把前面的操作保存成一个 Layer 存入栈中,再新建一个 Layer,后续绘制在新的 Layer 上进行,新绘制的 Layer 可以根据 saveLayer 的时候传的参数进行选择保存上一个 Layer 的哪些 Matrix 以及 Clip 剪裁的变换。
目前个人除了在使用 Xfermode 的时候,用 save() 方法的话,原先绘制的背景会对混合结果造成影响,所以要用 saveLayer()。其他地方使用暂时没有发现区别。
