(十)Canvas 的基本使用

xiaoxiao2021-02-28  35

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、概述

Canvas 我们经常把它理解为画板,但是,实际上 Canvas 是一个封装类,它包含了四个东西:

1、画板:用来保存像素的bitmap

2、画布或者画纸: Canvas 在 Bitmap 上进行绘制操作 —- (Layer—saveLayer操作时,新建一个透明的画布图层)

3、绘制的东西

4、绘制的画笔 Paint

二、Canvas 的基本方法

1.画直线

canvas.drawLine(0, 0, 100, 100, paint);

drawLine(float startX, float startY, float stopX, float stopY, Paint paint) startX: 起始点 x 坐标 startY :起始点 y坐标 stopX: 结束点 x 坐标 stopY :结束点 y坐标 paint :画笔

2.画多条直线

float []pts = {0,0,100,100,200,200,300,300}; canvas.drawLines(pts, paint);

drawLines(float[] pts, Paint paint) pts : 直线的端点数组,每 4 个点为一条直线

drawLines(float[] pts, int offset, int count, Paint paint) pts : 直线的端点数组,每 4 个点为一条直线 offset : 跳过的数据个数,跳过的数据不参与直线绘制 count : 实际参与绘制的数据个数

3.画点

canvas.drawPoint(50, 50, paint);

drawPoint(float x, float y, Paint paint) x : 点的 x 坐标 y : 点的 y 坐标

4.画多个点

float []pts = {0,0,100,100,200,200,300,300}; canvas.drawPoints(pts, paint);

drawPoints(float[] pts, Paint paint) pts : 点的坐标数组,每 2个点为一个点

drawPoints(float[] pts, int offset, int count, Paint paint) pts : 点的坐标数组,每 2个点为一个点 offset : 跳过的数据个数,跳过的数据不参与点绘制 count : 实际参与绘制的数据个数

5.画矩形

RectF r = new RectF(100, 100, 400, 500); canvas.drawRect(r, paint); canvas.drawRectdrawRect(100, 100, 400, 500, paint);

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。

6.画圆角矩形

RectF r = new RectF(100, 100, 400, 500); canvas.drawRoundRect(r, 30, 30, paint); canvas.drawRoundRect(100, 100, 400, 500, 30, 30, paint);

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方向上的圆角半径。

7.画圆

canvas.drawCircle(300, 300, 200, paint);

drawCircle(float cx, float cy, float radius, Paint paint) cx : 圆心的x坐标 cy : 圆心的y坐标 radius : 圆的半径

8.画椭圆

RectF r = new RectF(100, 100, 400, 500); canvas.drawOval(r, paint); canvas.drawOval(100, 100, 400, 500, 30, 30, paint);

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 坐标

9.画圆弧

RectF r = new RectF(100, 100, 400, 500); canvas.drawArc(r, 0, 90, true, paint); canvas.drawArc(100, 100, 400, 500, 0, 90, true, paint);

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:

10.画路径

Path path = new Path(); //画笔落笔的位置 path.moveTo(100, 100); //移动 path.lineTo(200, 100); path.lineTo(200, 200); path.cubicTo(250, 200, 400, 250, 450, 400); path.close(); canvas.drawPath(path, paint);

drawPath(Path path, Paint paint) path : 路径

path 部分不在这边细讲。

三、Canvas 画区域

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 : 叠加规则

四、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); paint.setColor(Color.BLUE); canvas.drawRect(r, paint);

上方代码先绘制一个矩形,然后进行平移操作,后再次绘制一个矩形。在 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 自己的坐标系、绘图坐标系。

1.Canvas 自己的坐标系

它就在View的左上角,做坐标原点往右是X轴正半轴,往下是Y轴的正半轴,有且只有一个,唯一不变。

2..Canvas 绘图坐标系

它不是唯一不变的,它与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来进行改变的

3.Canvas 绘图坐标系的重置

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, 再次进行坐标变化是从最开始的坐标系进行变化。说明第一次变化后,坐标系重置还原了。

五、Canvas的状态保存—状态栈、Layer栈

1.状态栈 – save、 restore 方法

1.只是保存和还原变换操作 Matrix 以及 Clip 剪裁, 2.可以通过 restoretoCount 直接还原到对应栈的保存状态。 3.不是真正的图层关系

2.Layer栈

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()。其他地方使用暂时没有发现区别。

转载请注明原文地址: https://www.6miu.com/read-600283.html

最新回复(0)