前言
上一篇讲 View 的绘制流程中讲到过, 最后一步是 draw 流程, 在这个过程中, 子 view 需要重写 onDraw 方法来画出自己的内容. 在自定义 View 绘制自身内容的时候, 系统提供了 3 个非常重要的类来帮助开发者画各种炫酷的图形: Canvas,Paint,Path. 本篇主要介绍 Canvas 相关的内容, Paint 和 Path 在后面会单独再做介绍. 官方文档中介绍的 Canvas 相关的 API 很多, 本文主要介绍和梳理一些比较常用的实用功能.
本文的主要内容如下:
一, 一切的开始 --onDraw
如下代码展示了一个自定义 view 画图形的一个非常简单的示例. 这里 onDraw 方法的参数 Canvas 就是本篇的主角了, 中文意思 "画布", 意思就是所有 "画" 的内容都是在这张画布上完成的.
- Paint paint = new Paint();
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 绘制一个原点坐标(300,300), 半径为 200 的圆
- canvas.drawCircle(300, 300, 200, paint);
- }
代码非常简单, 可见自定义绘制上手非常容易.
二, Canvas 基本的 draw 功能
Canvas 包含了一些画基本图案的函数, 基本都是以 drawXXX 的形式给出的.
1, 坐标系
这里先简单介绍一下画图中所参照的坐标系, 是以当前 View 位置的左上角为原点(0,0), 水平方向向右为 X 轴正方向, 竖直向下为 Y 轴正方向, 这里的 View 位置是个相对值, 它取决于开发者把它放在哪里. 坐标系大致如下所示, 注意和平时我们数学上坐标系略有差别.
2,Paint
在 Canvas 的这些 drawXXX 方法中会用到 paint 类, 它是画笔, 会决定绘制出来的图形的颜色, 是填充整个图形还是仅绘制边框线条等多个属性. 这些会在以后专门介绍 Paint 的时候再讲, 这里先提醒读者注意, 在笔者截取的效果图中, 咱们这里先只关注形状, 关于 piant, 一律先用默认的填充.
3,drawColor()颜色填充
(1)函数原型: drawColor(@color int color).
(2)作用: 将整个绘制区域填充为指定颜色(可以设置透明度).
(3)示例: 一般有三种方式来引用颜色值
- // 使用系统提供的颜色
- canvas.drawColor(Color.BLACK);
- // 使用自定义颜色
- canvas.drawColor(Color.parseColor("#000000"));
- // 使用 color.xml 中定义的颜色值
- canvas.drawColor(getResources().getColor(R.color.black));
(4)效果:
(5)其它设置颜色的方法
1)drawRGB(int r, int g, int b): 根据 RGB 值来设置颜色
2)drawARGB(int a, int r, int g, int b): 根据透明度和 RGB 的值来设置颜色.
上述中的几种方法, 适用于在绘制前设置底色, 或者在绘制完成后添加一层半透明蒙板.
4,drawCircle 画圆
(1)函数原型 drawCircle(float centerX, float centerY, float radius, Paint paint). 参数依次为: X 坐标, Y 坐标, 半径, 画笔.
(2)作用: 以原点(centerX,centerY), 半径 radius, 画一个圆.
(3)示例:
- // 绘制一个原点坐标(300,300), 半径为 200 的圆
- canvas.drawCircle(300, 300, 200, paint);
(4)效果
5,drawRect 画矩形
(1)函数原型: drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) 参数分别为左边, 顶部, 右边, 底部四条边的坐标值.
(2)根据给定的 4 条边框的坐标, 来画矩形.
(3)示例:
1 canvas.drawRect(100, 100, 500, 500, paint);
(4)效果
(5)其他函数
1) drawRect(@NonNull Rect r, @NonNull Paint paint). 实际上就是将前面函数定义为一个 Rect 来作为参数, 作用和上面一样.
2)示例, 效果和上述一样.
1 canvas.drawRect(new Rect(100,100,500,500),mPaint);
6,drawPoint 画点
(1)函数原型: drawPoint(float x, float y, @NonNull Paint paint). 参数 x,y 分别为该点的坐标, 点的大小和形状由 paint 决定.
(2)功能: 在指定 x,y 坐标处画点
(3)示例
1 canvas.drawPoint(50, 50, paint);
(4)效果(和画圆很类似, 其实也可以用来画圆)
(5)画多个点
1)drawPoints(float[] pts, int offset, int count,@NonNull Paint paint).pts 这个数组是点的坐标, 每两个成一对; offset 表示跳过数组的前几个数再开始记坐标; count 表示一共有多个数参与绘制.
2)示例:
- float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50};
- canvas.drawPoints(points, 2 /* 跳过两个数, 即前两个 0 */,
- 8 /* 一共绘制 8 个数(4 个点)*/, paint);
3)效果. 效果图中 4 个点的坐标分别为(50,50),(50,100),(100,50),(100,100)
4)drawPoints( @NonNull float[] pts, @NonNull Paint paint); 就是默认将上述函数的 offset 设置为 0,count 设置为所有数据参与绘制.
7,drawOval 画椭圆
(1)函数原型: drawOval(float left, float top, float right, float bottom, @NonNull Paint paint). 参数分别为四个顶点所在切线坐标.
(2)作用: 4 个参数确定了一个矩形的 4 条边框, 这个矩形确定了一个椭圆. 该函数用于画出这个指定的椭圆.
(3)示例
1 canvas.drawOval(50, 50, 350, 200, paint);
(4)效果
(5)其它构造函数 : drawOval(RectF rect, Paint paint)
8,drawLine 画直线
(1)函数原型: drawLine(float startX, float startY, float stopX, float stopY, Paint paint). 参数值分别为起始点坐标和结束点坐标.
(2)作用: 根据给定的起始点和结束点画直线
(3)示例
1 canvas.drawLine(200, 200, 800, 500, paint);
(4)效果
(5) 批量画线: drawLines(float[] pts, int offset, int count,Paint paint) / drawLines(float[] pts, Paint paint). 使用方法参照批量画圆.
9,drawRoundRect 画圆角矩形
(1)函数原型: drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint).
(2)该函数的参数, 作用, 构造函数等可以参看矩形的绘制. 不同点是多了 rx 和 ry, 它们表示顶点圆角的 X 轴方向半径和 Y 轴方向半径.
(3)示例
1 canvas.drawRoundRect(100, 100, 500, 300, 50, 50, paint);
(4)效果
10,drawArc 画弧线或扇形
(1)函数原型: drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint). 扇形或者弧线, 都是椭圆 (圆可以看成是椭圆的特殊情形) 上的一部分, 所以画扇形或者弧线需要先确定好椭圆, 前 4 个参数就是用于确定这个椭圆. startAngle 表示起始角度, X 轴正方向 (即水平向右方向) 为 0 度, 顺时针为正角度, 逆时针为负角度. sweepAngle 表示弧线 / 扇形划过的角度, 依然是顺时针为正, 逆时针为负; useCenter 表示是否连到圆心, true 表示连到, 表示扇形, false 表示不连到, 表示弧线.
(2)示例:
- canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // 绘制扇型
- canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint); // 绘制弧形
(3)效果
11,drawPath 画自定图形
(1)函数原型: drawPath(Path path, Paint paint)
(2)根据 path 定义的形状来绘制. 这个会在后续文章中详细讲.
12,drawBitmap
(1)函数原型: drawBitmap(Bitmap bitmap, float left, float top, Paint paint). 参数 bitmap 表示要绘制的图片对象; left,top 表示要显示的 bitmap 对象的左上角左边位置.
(2)作用: 把指定的 bitmap 对象中的像素复制到指定位置.
(3)示例:
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);
- canvas.drawBitmap(bitmap,100,50,paint);
(4)效果
(5)重载方法
- drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint);
- drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint);
- drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint);
它们可以指定复制原 bitmap 中的指定区域 (Rect src 来确定), 到画布中的指定位置(Rect dst) 来确定.
13,drawText 绘制文字
函数原型: drawText(String text, float x, float y, Paint paint). 界面里所有的显示内容, 都是绘制出来的, 包括文字. drawText() 这个方法就是用来绘制文字的. 参数 text 是用来绘制的字符串, x 和 y 是绘制的起点坐标. 文字的绘制在后面也会单独详细说明.
三, Canvas 实现裁剪
根据给定的范围, 对指定的 bitmap 进行裁剪, 裁剪之后再进行绘制. 范围裁剪有两个方法: clipRect()和 clipPath(). 前者会截取一个矩形范围, 而后者 path 指定的形状更多, 所以裁剪的形状也会更多.
1,Canvas.save()和 Canvas.restore()
在裁剪的过程中, 要加上这两句代码. 它们用于保存和恢复绘制范围, 如果不加容易出现干扰.
- 2,clipRect()
- canvas.save();
- // 目标 bitmap
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);
- // 确定需要裁剪的矩形范围
- canvas.clipRect(left, top, right, bottom);
- // 在指定的 x,y 坐标开始绘制裁剪后的 bitmap
- canvas.drawBitmap(bitmap, x, y, paint);
- canvas.restore();
裁剪后的效果如下图所示:
3,clipPath()
Canvas.clipRect(Path path) 用法完全一样, 只是把参数换成了 Path , 所以能裁切的形状更多一些. 其对应有一个方法 Canvas.clipOutPaht(Path path), 反向裁剪, 效果可以从截图中看出来.
- canvas.save();
- canvas.clipPath(path1);
- canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
- canvas.restore();
- canvas.save();
- canvas.clipOutPath(path);
- canvas.drawBitmap(bitmap, point2.x, point2.y, paint);
- canvas.restore();
这个功能对显示原型头像比较有帮助.
四, Canvas 实现几何变换
几何变换的使用大概分为三类: 使用 Canvas 来做常见的二维变换; 使用 Matrix 来做常见和不常见的二维变换; 使用 Camera 来做三维变换.
1, 使用 Canvas 来做常见的二维变换
(1)Canvas.translate(float dx, float dy) 平移. 参数里的 dx 和 dy 表示横向和纵向的位移.
(2)Canvas.rotate(float degrees, float px, float py) 旋转
(3)Canvas.scale(float sx, float sy, float px, float py)放大缩小
(4)Canvas.skew(float sx, float sy) 错切, 就是倾斜的意思.
- canvas.save();
- // 水平向 X 轴正方向移动 200px
- canvas.translate(200, 0);
- // 以给定的点为轴旋转 45 度
- canvas.rotate(45, centerX, centerY);
- // 以图片中心为轴, 水平和竖直方向各放大为原来的 1.3 倍
- canvas.scale(1.3f, 1.3f, x + bitmapWidth / 2, y + bitmapHeight / 2);
- canvas.skew(0, 0.5f);
- canvas.drawBitmap(bitmap, x, y, paint);
- canvas.restore();
2, 使用 Matrix 来做变换
使用 Matrix 可以做常规的几何变换, 还可以对图形进行自定义变换. 在做常规的几何变换时, 和前面介绍的用 Canvas 实现常规变换, 功能一样, 选其一即可.
(1)使用 Matrix 做常规变换
使用 Matrix 来做常规变换主要分三个步骤 (1) 创建 Matrix 对象;(2)Matrix.pre/postTranslate/Rotate/Scale/Skew()设置几何变换;(3)Canvas.setMatrix(matrix)或 Canvas.concat(matrix)把几何变换应用到 Canvas 中(尽量用后者).
- //1. 创建 Matrix 对象
- Matrix matrix = new Matrix();
- matrix.reset();
- //2. 几何变换
- matrix.postTranslate(0,100);
- matrix.postRotate(45,bitmap.getWidth()/2,bitmap.getHeight()/2);
- matrix.postScale(1.3f,1.3f,x+bitmap.getWidth()/2,y+bitmap.getHeight()/2);
- matrix.postSkew(0,0.5f);
- canvas.save();
- //3. 把几何变换应用到 Canvas 中
- canvas.concat(matrix);
- canvas.drawBitmap(bitmap,x,y,paint1);
- canvas.restore();
(2)使用 Matrix 做自定义变换
使用 Maxtrix 做自定义变换, 可以对图形进行任意拉伸, 如下图所示:
这需要用到 Matrix 提供的 setPolyToPoly 方法来实现, 具体的使用这里不做介绍, 请查阅[个人记录 View Matrix setPolyToPoly] .
3, 使用 Camera 来做三维变换
这一块没有使用过, 这里不做介绍, 知道有这个功能即可.
结语
本文主要就是记录和梳理学习 Canvas 中的一些要点, 非常基础, 但都实用和常见. 内容的来源是腾讯课堂中 "仍物线学堂" 中课件, 特在此声明.
来源: https://www.cnblogs.com/andy-songwei/p/10960012.html