【朝花夕拾】Android自定义View篇之(二)Canvas常用功能
前言
转在请申明,转自【https://www.cnblogs.com/andy-songwei/p/10960012.html】,谢谢!
上一篇讲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)示例:
1 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.test);
2 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中的一些要点,非常基础,但都实用和常见。内容的来源是腾讯课堂中“仍物线学堂”中课件,特在此声明。
【朝花夕拾】Android自定义View篇之(二)Canvas常用功能的更多相关文章
- 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介
前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...
- 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...
- 【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10979161.html],谢谢! 尽管Android系统提供了不少控件,但是有很多酷炫效果仍然 ...
- 【朝花夕拾】Android自定义View篇之(一)View绘制流程
前言 转载请申明转自[https://www.cnblogs.com/andy-songwei/p/10955062.html]谢谢! 自定义View.多线程.网络,被认为是Android开发者必须牢 ...
- 【朝花夕拾】Android自定义View篇之(九)多点触控(下)实践出真知
前言 在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数.本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识.要理 ...
- 【朝花夕拾】Android自定义View篇之(十一)View的滑动,弹性滑动与自定义PagerView
前言 由于手机屏幕尺寸有限,但是又经常需要在屏幕中显示大量的内容,这就使得必须有部分内容显示,部分内容隐藏.这就需要用一个Android中很重要的概念——滑动.滑动,顾名思义就是view从一个地方移动 ...
- 【朝花夕拾】Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/10998855.html]谢谢! 在自定义View中,经常需要处理Android事件分发的问题, ...
- 【朝花夕拾】Android自定义View篇之(十)TouchSlop及VelocityTracker
前言 在Android事件中,有几个比较基本的概念和知识点需要掌握.比如,表示最小移动阈值的TouchSlop,追踪事件速度的VelocityTracker,用于检测手势的GestureDetecto ...
- Android自定义View学习(二)
绘制顺序 参考:HenCoder Android 开发进阶:自定义 View 1-5 绘制顺序 绘制过程 包括 背景 主体(onDraw()) 子 View(dispatchDraw()) 滑动边缘渐 ...
随机推荐
- webpack学习_模块热替换(Hot Module Peaplacement)
模块热替换(Hot Module Replacement 或 HMR) 是webpack提供的最有用的功能之一.允许在u女性是更新各种模块,而无需进行完全刷新. 启用HMR 承接之前的代码 webpa ...
- ajax数据交互
目录 一.ORM查询优化 1-1. only与defer 1-2. select_related与prefatch_related 二.MTV与MVC模型 三.choices参数 四.AJAX 4-1 ...
- springcloud-微服务架构基础
一 前言 学习微服务要从基础的架构学起,首先你要有个微服务的概念才能学习对吧!!如果你都不知道啥是微服务,就一头扎进去学习,你自己也觉得自己也学不会对吧.本篇文章主要让大家快速了解基础的架构分格,以便 ...
- 松软科技Web课堂:JavaScript For 循环
循环可多次执行代码块. JavaScript 循环 假如您需要运行代码多次,且每次使用不同的值,那么循环(loop)相当方便使用. 通常我们会遇到使用数组的例子: 不需要这样写: text += ca ...
- 「SAP技术」SAP VL02N 执行批次拆分报错,说不允许批次拆分?
1,如下新建的DN 80017843,storage location 字段值为空.VL02N 试图去做批次拆分失败,系统报错说,Batch split is not permitted for ma ...
- AI: Web: 1 Vulnhub Walkthrough
下载链接: https://www.vulnhub.com/entry/ai-web-1,353/ 主机发现扫描: 主机端口扫描 http://10.10.202.158/ 目录扫描: ╰─ sudo ...
- Android 列表对话框 setItems
private Button button; private final CharSequence[] items = { "北京", "上海", " ...
- 1001 害死人不偿命的(3n+1)猜想 (15 分)
卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复砍下去,最后一定在某一步得到 n=1.卡拉兹在 1950 ...
- 利用Fiddler对Jmeter的请求进行抓包
前言 有时候,为了得到更详细的请求结果,我们可能需要使用Fiddler结合Jmeter来抓包分析,从而更好的辅助测试. 遇到的问题 这里以一个获取学生信息的接口为例进行说明. 当我在Jmeter里按接 ...
- MySQL实现统计数据并插入数据的存储过程
统计存储过程,这里是将统计的结果插入一个表中,后台可以有定时任务来调此存储过程.以下业务是统计仓库中商品流转情况,包括:日期.商品总数.入库数量.出库数量. BEGIN DECLARE ES_COR_ ...