前面讲解了onMeasure,接下来讲解onDraw,onDraw主要就是绘制,也就是我们真正关心的部分,使用的是Canvas绘图。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
Canvas简介
Canvas我们可以称之为画布,能够在上面绘制各种东西,是安卓平台2D图形绘制的基础,非常强大。
Canvas的常用操作速查表
操作类型 | 相关API | 备注 |
---|---|---|
绘制颜色 | drawColor、drawRGB、drawARGB | 使用单一颜色填充整个画布 |
绘制基本形状 | drawPoint、 drawPoints、 drawLine、 drawLines、 drawRect、 drawRoundRect、 drawOval、 drawCircle、 drawArc | 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 |
绘制图片 | drawBitmap、 drawPicture | 绘制位图和图片 |
绘制文本 | drawText、 drawPosText、 drawTextOnPath | 依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字 |
绘制路径 | drawPath | 绘制路径,绘制贝塞尔曲线时也需要用到该函数 |
顶点操作 | drawVertices、 drawBitmapMesh | 通过对顶点操作可以使图像形变,drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用 |
画布剪裁 | clipPath、 clipRect | 设置画布的显示区域 |
画布快照 | save、 restore、 saveLayerXxx、 restoreToCount、 getSaveCount | 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 |
画布变换 | translate、 scale、 rotate、 skew | 依次为 位移、缩放、 旋转、错切 |
Matrix(矩阵) | getMatrix、 setMatrix、 concat | 实际上画布的位移,缩放等操作的都是图像矩阵Matrix,只不过Matrix比较难以理解和使用,故封装了一些常用的方法。 |
PS: Canvas常用方法在上面已经列出,当然还存在一些其他的方法未列出,具体可以参考官方文档 Canvas
Canvas详解
本篇内容主要讲解如何利用Canvas绘制基本图形。
- 创建画笔:
要想绘制内容,首先需要先创建一个画笔,如下:
// 1.创建一个画笔
private Paint mPaint = new Paint();
// 3.在构造函数中初始化
public SloopView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
// 2.初始化画笔
private void initPaint() {
mPaint.setColor(Color.BLACK); //设置画笔颜色
mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充
mPaint.setStrokeWidth(10f); //设置画笔宽度为10px
}
在创建完画笔之后,就可以在Canvas中绘制各种内容了。
- 绘制矩形:
确定确定一个矩形最少需要四个数据,就是对角线的两个点的坐标值,这里一般采用左上角和右下角的两个点的坐标。
关于绘制矩形,Canvas提供了三种重载方法,第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。 其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制,如下:
// 第一种
canvas.drawRect(100,100,800,400,mPaint);
// 第二种
Rect rect = new Rect(100,100,800,400);
canvas.drawRect(rect,mPaint);
// 第三种
RectF rectF = new RectF(100,100,800,400);
canvas.drawRect(rectF,mPaint);
以上三种方法所绘制出来的结果是完全一样的。
为什么会有Rect和RectF两种?两者有什么区别吗?
答案是肯定有区别,两者最大的区别就是精度不同,Rect是int(整形)的,而RectF是float(单精度浮点型)的。除了精度不同,两种提供的方法也稍微存在差别,想了解更多参见官方文档 Rect 和 RectF
- 绘制圆角矩形:
RectF rectF = new RectF(100,100,800,400);
canvas.drawRoundRect(rectF,30,30,mPaint);
这里多出来的两个参数是椭圆的两个半径,绘制出的圆弧是椭圆的圆弧.
红线标注的 rx 与 ry 就是两个椭圆半径,也就是相比绘制矩形多出来的那两个参数。
我们了解到原理后,就可以为所欲为了,通过计算可知我们上次绘制的矩形宽度为700,高度为300,当你让 rx大于350(宽度的一半), ry大于150(高度的一半) 时奇迹就出现了, 你会发现圆角矩形变成了一个椭圆, 他们画出来是这样的 ( 为了方便确认我更改了画笔颜色, 同时绘制出了矩形和圆角矩形 ):
// 矩形
RectF rectF = new RectF(100,100,800,400);
// 绘制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 绘制圆角矩形
mPaint.setColor(Color.BLUE);
canvas.drawRoundRect(rectF,700,400,mPaint);
其中灰色部分是我们所绘制的矩形(参考用),而里面的圆角矩形则变成了一个椭圆,实际上在rx为宽度的一半,ry为高度的一半时,刚好是一个椭圆,通过上面我们分析的原理推算一下就能得到,而当rx大于宽度的一半,ry大于高度的一半时,实际上是无法计算出圆弧的,所以drawRoundRect对大于该数值的参数进行了限制(修正),凡是大于一半的参数均按照一半来处理。
- 绘制椭圆:
相对于绘制圆角矩形,绘制椭圆就简单的多了,因为他只需要一个矩形矩形作为参数:
// 第一种
RectF rectF = new RectF(100,100,800,400);
canvas.drawOval(rectF,mPaint);
// 第二种
canvas.drawOval(100,100,800,400,mPaint);
同样,以上两种方法效果完全一样,但一般使用第一种。
PS: 如果你传递进来的是一个长宽相等的矩形(即正方形),那么绘制出来的实际上就是一个圆。
-
绘制圆:
绘制圆形也比较简单, 如下:
canvas.drawCircle(500,500,400,mPaint); // 绘制一个圆心坐标在(500,500),半径为400 的圆。
绘制圆形有四个参数,前两个是圆心坐标,第三个是半径,最后一个是画笔。
绘制圆弧:
绘制圆弧就比较神奇一点了,为了理解这个比较神奇的东西,我们先看一下它需要的几个参数:
// 第一种
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle,
boolean useCenter, @NonNull Paint paint){}
// 第二种
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {}
从上面可以看出,相比于绘制椭圆,绘制圆弧还多了三个参数:
startAngle // 开始角度
sweepAngle // 扫过角度
useCenter // 是否使用中心
通过字面意思我们基本能猜测出来前两个参数(startAngle, sweepAngel)的作用,就是确定角度的起始位置和扫过角度, 不过第三个参数是干嘛的?试一下就知道了,上代码:
RectF rectF = new RectF(100,100,800,400);
// 绘制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 绘制圆弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF,0,90,false,mPaint);
//-------------------------------------
RectF rectF2 = new RectF(100,600,800,900);
// 绘制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF2,mPaint);
// 绘制圆弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF2,0,90,true,mPaint);
上述代码实际上是绘制了一个起始角度为0度,扫过90度的圆弧,两者的区别就是是否使用了中心点,结果如下:
相比于使用椭圆,我们还是使用正圆比较多的,使用正圆展示一下效果:
RectF rectF = new RectF(100,100,600,600);
// 绘制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF,mPaint);
// 绘制圆弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF,0,90,false,mPaint);
//-------------------------------------
RectF rectF2 = new RectF(100,700,600,1200);
// 绘制背景矩形
mPaint.setColor(Color.GRAY);
canvas.drawRect(rectF2,mPaint);
// 绘制圆弧
mPaint.setColor(Color.BLUE);
canvas.drawArc(rectF2,0,90,true,mPaint);
简要介绍Paint
看了上面这么多,相信有有人会有疑问,如果我想绘制一个圆,只要边不要里面的颜色怎么办?
很简单,绘制的基本形状由Canvas确定,但绘制出来的颜色,具体效果则由Paint确定。
如果你注意到了的话,在一开始我们设置画笔样式的时候是这样的:
mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充
为了展示方便,容易看出效果,之前使用的模式一直为填充模式,实际上画笔有三种模式,如下:
STROKE //描边
FILL //填充
FILL_AND_STROKE //描边加填充
为了区分三者效果我们做如下实验:
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(40); //为了实验效果明显,特地设置描边宽度非常大
// 描边
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(200,200,100,paint);
// 填充
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(200,500,100,paint);
// 描边加填充
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(200, 800, 100, paint);
关于Paint详细的可以看下官方介绍Paint