Android 自定义控件或多或少都会用到Canvas,那么我们就需要熟悉它的API。Canvas给我们提供了大量的的DrawXXX方法,通过这些方法我们就可以绘制出我们想要的效果。接下来看看官方是怎么说的:
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
大致意思就是说:
Canvas类持有“draw”调用。 要绘制一些东西,你需要4个基本组件:一个位图来保存像素,一张画布(Canvas)来主持绘图调用(写入位图),一个绘图图元(如Rect,Path,text,Bitmap)和一支画笔(描述绘图的颜色和样式)。
首先介绍下画笔(Paint)的常用API:
private void initPaint() {
// 设置最基本的属性
// 设置画笔颜色
// 可直接引入Color类,如Color.red等
mPaint.setColor(int color);
// 设置画笔模式
mPaint.setStyle(Style style);
// Style有3种类型:
// 类型1:Paint.Style.FILLANDSTROKE(描边+填充)
// 类型2:Paint.Style.FILL(只填充不描边)
// 类型3:Paint.Style.STROKE(只描边不填充)
//设置画笔的粗细
mPaint.setStrokeWidth(float width);
// 如设置画笔宽度为10px
mPaint.setStrokeWidth(10f);
// 不常设置的属性
// 得到画笔的颜色
mPaint.getColor();
// 设置Shader
// 即着色器,定义了图形的着色、外观
mPaint.setShader(Shader shader);
//设置画笔的a,r,p,g值
mPaint.setARGB(int a, int r, int g, int b);
//设置透明度
mPaint.setAlpha(int a);
//得到画笔的Alpha值
mPaint.getAlpha();
// 对字体进行设置(大小、颜色)
//设置字体大小
mPaint.setTextSize(float textSize)
// 设置对齐方式
mPaint.setTextAlign(Algin algin);
// LEFT:左对齐
// CENTER:居中对齐
// RIGHT:右对齐
//设置文本的下划线
mPaint.setUnderlineText(boolean underlineText);
//设置文本的删除线
mPaint.setStrikeThruText(boolean strikeThruText);
//设置文本粗体
mPaint.setFakeBoldText(boolean fakeBoldText);
// 设置斜体
mPaint.setTextSkewX(-0.5f);
// 设置文字阴影
mPaint.setShadowLayer(5,5,5,Color.YELLOW);
}
Paint默认的字体大小为12px,在绘制文本时我们往往要考虑密度density设置合适的字体大小。画笔的默认颜色为黑色,默认的style为FILL,默认的cap为BUTT,默认的线宽为0。
一 坐标系
- Canvas坐标系
Canvas坐标系即Canvas本身的坐标系。Canvas坐标原点(0,0)在View的左上角,向右为X轴正方向,向下为Y轴正方向。Canvas坐标系唯一且不会改变。 - 绘图坐标系
绘图坐标系区别于Canvas坐标系,以我们绘图起始点为坐标原点(0,0),绘图坐标系默认与Canvas坐标系重合。绘图坐标系原点位置可以更改,Canvas的 translate、rotate、scale 操作都是基于绘图坐标系变化。
//绘制坐标系
private void drawAxis(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int translate = width / 5;
//设置画笔宽度
mPaint.setStrokeWidth(10);
//设置笔触样式
mPaint.setStrokeCap(Paint.Cap.ROUND);
//绘制默认绘图坐标系(此时与Canvas坐标系重合)
//绘制红色X轴
mPaint.setColor(Color.RED);
canvas.drawLine(0, 0, width, 0, mPaint);
//绘制蓝色Y轴
mPaint.setColor(Color.BLUE);
canvas.drawLine(0, 0, 0, height, mPaint);
//平移
canvas.translate(translate, translate);
//绘制平移后绘图坐标系
//绘制红色X轴
mPaint.setColor(Color.RED);
canvas.drawLine(0, 0, width, 0, mPaint);
//绘制蓝色Y轴
mPaint.setColor(Color.BLUE);
canvas.drawLine(0, 0, 0, height, mPaint);
//平移 旋转30度
canvas.translate(translate, translate);
canvas.rotate(30);
//绘制平移旋转后绘图坐标系
//绘制红色X轴
mPaint.setColor(Color.RED);
canvas.drawLine(0, 0, width, 0, mPaint);
//绘制蓝色Y轴
mPaint.setColor(Color.BLUE);
canvas.drawLine(0, 0, 0, height, mPaint);
}
二 Canvas绘制颜色
单一颜色填充Canvas画布
- drawColor
- drawRGB
- drawARGB
//绘制画布颜色
private void drawCanvasColor(Canvas canvas) {
//drawARGB
canvas.drawARGB(255, 47, 140, 150);
}
三 绘制点
- drawPoint 绘制点
- drawPoints 绘制一组点
//绘制点
private void drawPoint(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int translateY = height/5;
//设置画笔颜色
mPaint.setColor(Color.RED);
//设置画笔宽度
mPaint.setStrokeWidth(200);
//设置笔触样式
mPaint.setStrokeCap(Paint.Cap.BUTT);
//保存当前画布状态(坐标系)
canvas.save();
canvas.drawPoint(width / 3, height / 3, mPaint);
//恢复上次画布保存状态(坐标系)
canvas.restore();
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setColor(Color.GREEN);
canvas.save();
canvas.translate(0, translateY);
canvas.drawPoint(width / 3, height / 3, mPaint);
canvas.restore();
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setColor(Color.BLUE);
canvas.save();
canvas.translate(translateY, translateY);
canvas.drawPoint(width / 3, height / 3, mPaint);
canvas.restore();
}
四 绘制线
- drawLine 绘制线
- drawLines 绘制一组线
//绘制线
private void drawLine(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int startX = width / 5;
int startY = height / 5;
//设置画笔颜色
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(20);
//设置笔触样式
mPaint.setStrokeCap(Paint.Cap.BUTT);
//画笔触为BUTT的线
canvas.drawLine(startX, startY, width - startX, startY, mPaint);
//向下平移20
canvas.translate(0, 60);
mPaint.setColor(Color.GREEN);
mPaint.setStrokeCap(Paint.Cap.ROUND);
//画笔触为ROUND的线
canvas.drawLine(startX, startY, width - startX, startY, mPaint);
canvas.translate(0, 60);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
//画笔触为SQUARE的线
canvas.drawLine(startX, startY, width - startX, startY, mPaint);
canvas.translate(0, 80);
mPaint.setColor(Color.BLACK);
float[] pts = new float[]{startX, startY, width - startX, startY, startX, startY + 60, width - startX, startY + 60};
canvas.drawLines(pts, mPaint);
}
五 绘制矩形
- drawRect 绘制矩形
- drawRoundRect 绘制圆角矩形
//绘制矩形
private void drawRect(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
//设置画笔颜色
mPaint.setColor(Color.RED);
//设置画笔宽度
mPaint.setStrokeWidth(16);
//设置画笔样式
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(20, 20, 400, 200, mPaint);
//绘制矩形 API21
//canvas.drawRoundRect(220, 220, 400, 400, 10, 10, mPaint);
RectF rect = new RectF(20, 420, 800, 600);
canvas.drawRoundRect(rect, 10, 10, mPaint);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(20, 800, 400, 1000, mPaint);
RectF rect1 = new RectF(20, 1200, 800, 1400);
canvas.drawRoundRect(rect1, 10, 10, mPaint);
}
六 绘制圆
- drawCircle 绘制圆
//绘制圆形
private void drawCircle(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int halfWidth = width / 2;
int d = height / 3;
int r = d / 2 - 10;
//设置画笔颜色
mPaint.setColor(Color.RED);
//设置画笔宽度
mPaint.setStrokeWidth(2);
//设置画笔样式
mPaint.setStyle(Paint.Style.FILL);
//绘制圆
canvas.drawCircle(halfWidth, (float) (d * 0.5), r, mPaint);
//绘制两个圆实现圆环效果
mPaint.setColor(Color.GREEN);
canvas.drawCircle(halfWidth, (float) (d * 1.5), r, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(halfWidth, (float) (d * 1.5), r - 40, mPaint);
//绘制一个圆实现圆环效果
mPaint.setStrokeWidth(40);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);
//半径减去画笔画笔宽度的一半 圆的大小和前面一致
canvas.drawCircle(halfWidth, (float) (d * 2.5), r - 20, mPaint);
}
七 绘制椭圆
- drawOval 绘制椭圆
跟绘制圆差不多,唯一不同就是绘制圆是指定圆心和半径,绘制椭圆是指定左上右下4个距离。
//绘制椭圆
private void drawOval(Canvas canvas) {
//设置画笔颜色
mPaint.setColor(Color.RED);
//设置画笔宽度
mPaint.setStrokeWidth(2);
//设置画笔样式
mPaint.setStyle(Paint.Style.FILL);
RectF overRect = new RectF(20, 20, 400, 200);
canvas.drawOval(overRect, mPaint);
mPaint.setColor(Color.GREEN);
mPaint.setStrokeWidth(20);
mPaint.setStyle(Paint.Style.STROKE);
overRect = new RectF(20, 220, 200, 600);
canvas.drawOval(overRect, mPaint);
}
八 绘制圆弧
- drawArc 绘制弧
绘制圆弧,可以绘制弧面和弧线。弧面即用弧围成的填充面,弧线即为弧面的轮廓线。
//绘制弧
private void drawAcr(Canvas canvas) {
int width = canvas.getWidth();
//设置画笔颜色
mPaint.setColor(Color.RED);
//设置画笔宽度
mPaint.setStrokeWidth(2);
//设置画笔样式
mPaint.setStyle(Paint.Style.FILL);
//和前面绘制椭圆的矩形一致(正方形也可)
RectF arcRect = new RectF(20, 20, 400, 200);
//根据矩形绘制弧形 开始角度0 扫过角度360 通过中心点首尾连接:true
// 和前面绘制的椭圆效果一致
canvas.drawArc(arcRect, 0, 360, true, mPaint);
canvas.translate(0, width / 5);
canvas.drawArc(arcRect, 0, 90, true, mPaint);
canvas.translate(0, width / 5);
canvas.drawArc(arcRect, 0, 90, false, mPaint);
//正方形
arcRect = new RectF(20, 20, 220, 220);
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.STROKE);
canvas.translate(0, width / 5);
//根据矩形绘制弧形 角度从0-1800 首尾连接
canvas.drawArc(arcRect, 0, 180, true, mPaint);
canvas.translate(0, width / 5);
//根据矩形绘制弧形 角度从0-1800 首尾不连接
canvas.drawArc(arcRect, 0, 180, false, mPaint);
mPaint.setStrokeWidth(10);
canvas.translate(width / 2, width / 5);
//drawArc绘制出圆环
canvas.drawArc(arcRect, 0, 270, false, mPaint);
mPaint.setColor(Color.GREEN);
canvas.drawArc(arcRect, 270, 90, false, mPaint);
}
九 绘制文字
- drawText 绘制文本
- drawPosText 根据位置绘制文本
- drawTextOnPath 根据路径绘制文本
//绘制文字
private void drawText(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int textHeight = 32;
int halfWidth = width / 2;
//设置画笔颜色
mPaint.setColor(Color.GRAY);
//设置画笔宽度
mPaint.setStrokeWidth(2);
canvas.drawLine(halfWidth, 0, halfWidth, height, mPaint);
mPaint.setColor(Color.BLACK);
//设置文本大小
mPaint.setTextSize(textHeight);
//绘制普通文本
canvas.drawText("绘制普通文本", 0, textHeight, mPaint);
mPaint.setColor(Color.RED);
mPaint.setTextAlign(Paint.Align.LEFT);
//存储画布状态 坐标系
canvas.save();
//平移
canvas.translate(halfWidth, textHeight);
//绘制左对齐文本
canvas.drawText("绘制左对齐文本", 0, textHeight, mPaint);
//恢复上次存储状态
canvas.restore();
mPaint.setColor(Color.GREEN);
mPaint.setTextAlign(Paint.Align.CENTER);
//存储画布状态 坐标系
canvas.save();
//平移
canvas.translate(halfWidth, textHeight * 2);
//绘制居中对齐文本(对齐是根据绘图坐标系而言)
canvas.drawText("绘制居中对齐文本", 0, textHeight, mPaint);
//恢复上次存储状态
canvas.restore();
mPaint.setColor(Color.BLUE);
mPaint.setTextAlign(Paint.Align.RIGHT);
//存储画布状态 坐标系
canvas.save();
//平移
canvas.translate(halfWidth, textHeight * 3);
//绘制右对齐文本
canvas.drawText("绘制右对齐文本", 0, textHeight, mPaint);
//恢复上次存储状态
canvas.restore();
mPaint.setColor(Color.BLACK);
//恢复默认对齐方式
mPaint.setTextAlign(Paint.Align.LEFT);
//设置下划线
mPaint.setUnderlineText(true);
//设置加粗
mPaint.setFakeBoldText(true);
//设置删除线
mPaint.setStrikeThruText(true);
//存储画布状态 坐标系
canvas.save();
//平移
canvas.translate(halfWidth, textHeight * 4);
//绘制下划线加粗文本
canvas.drawText("绘制下划线加粗文本", 0, textHeight, mPaint);
//恢复上次存储状态
canvas.restore();
mPaint.reset();
mPaint.setColor(Color.BLACK);
//设置文本大小
mPaint.setTextSize(textHeight);
//存储画布状态 坐标系
canvas.save();
canvas.translate(width / 3, height / 3);
//旋转
canvas.rotate(45);
//绘制倾斜文本
canvas.drawText("绘制倾斜文本", 0, textHeight, mPaint);
//恢复上次存储状态
canvas.restore();
float[] pos = new float[]{40, 40, 80, 80, 120, 120, 160, 160, 200, 200, 240, 240};
//存储画布状态 坐标系
canvas.save();
canvas.translate(width / 3, height / 2);
canvas.drawPosText("绘制位置文本", pos, mPaint);
//恢复上次存储状态
canvas.restore();
Path path = new Path();
path.cubicTo(550, 750, 350, 250, 850, 800);
//画路径
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path,mPaint);
canvas.drawTextOnPath("绘制文本OnPath绘制文本OnPath绘制文本OnPath绘制文本OnPath绘制文本OnPath", path, 50, 0, mPaint);
}
十 绘制Path
- drawPath 绘制路径
//path拐点集合
List<Point> points = new ArrayList<>();
//绘制Path
private void drawPath(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int deltaX = width / 4;
int deltaY = (int) (deltaX * 0.75);
//设置画笔颜色
mPaint.setColor(Color.GRAY);
//设置画笔宽度
mPaint.setStrokeWidth(4);
//Fill模式画面
Path path1 = new Path();
RectF arcRect = new RectF(0, 0, deltaX, deltaY);
path1.addArc(arcRect, 0, 360);
path1.addRect(deltaX, 0, deltaX << 1, deltaY, Path.Direction.CW);
canvas.drawPath(path1, mPaint);
//STROKE模式画线
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
canvas.translate(0, deltaY << 1);
canvas.drawPath(path1, mPaint);
mPaint.setColor(Color.BLUE);
// mPaint.setStyle(Paint.Style.FILL);
canvas.translate(0, deltaY << 1);
Path path2 = new Path();
path2.lineTo(100, 0); //画线
points.add(new Point(100, 0)); //终点 X: 100 Y: 0
RectF overRect = new RectF(100, -50, 300, 50);
path2.arcTo(overRect, 180, -90); //画椭圆 左下部分
points.add(new Point(200, 50)); //终点 X: 250 Y: 50
overRect = new RectF(200, 0, 400, 100);
path2.arcTo(overRect, 180, 90); //画椭圆右上部分
points.add(new Point(300, 0)); //终点 X: 300 Y: 0
path2.lineTo(350, 50); //画线
points.add(new Point(350, 50)); //终点 X: 350 Y: 50
//画二阶贝塞尔曲线 第一个坐标为控制点 最后为终点
path2.quadTo(450, -50, 500, 150);
points.add(new Point(500, 150)); //终点 X: 500 Y: 150
//画三阶阶贝塞尔曲线 前两个坐标为控制点 最后为终点
path2.cubicTo(600, 150, 700, 0, 380, -200);
points.add(new Point(380, -200)); //终点 X: 480 Y: -200
path2.rLineTo(0, 100);
points.add(new Point(380, -200 + 100)); //终点 X: 480 Y: -100
canvas.drawPath(path2, mPaint);
mPaint.setStrokeWidth(10);
mPaint.setColor(Color.RED);
mPaint.setStrokeCap(Paint.Cap.ROUND);
for (Point point : points) {
canvas.drawPoint(point.x, point.y, mPaint);
}
}
十一 绘制Bitmap
- drawBitmap 绘制Bitmap
//绘制Bitmap
private void drawBitmap(Canvas canvas) {
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.mipmap.ic_launcher);
Bitmap bitmap = drawable.getBitmap();
//如果bitmap不存在,那么就不执行下面的绘制代码
if (bitmap == null) {
return;
}
//直接完全绘制Bitmap
canvas.drawBitmap(bitmap, 0, 0, mPaint);
//绘制Bitmap的一部分,并对其拉伸
//srcRect定义了要绘制Bitmap的哪一部分
Rect srcRect = new Rect();
srcRect.left = 0;
srcRect.right = bitmap.getWidth();
srcRect.top = 0;
srcRect.bottom = (int) (0.33 * bitmap.getHeight());
float radio = (float) (srcRect.bottom - srcRect.top) / bitmap.getWidth();
//dstRecF定义了要将绘制的Bitmap拉伸到哪里
RectF dstRecF = new RectF();
dstRecF.left = 0;
dstRecF.right = canvas.getWidth();
dstRecF.top = bitmap.getHeight();
float dstHeight = (dstRecF.right - dstRecF.left) * radio;
dstRecF.bottom = dstRecF.top + dstHeight;
canvas.drawBitmap(bitmap, srcRect, dstRecF, mPaint);
}
以上就是大部分的绘制操作,当然还有一些没介绍到,还有一些需要在API21(5.0)以上才能使用,这个使用到的时候可以弄个小demo看下效果,接下来介绍下针对画布变换和画布裁剪做一些介绍。
十二 画布变换
- translate 平移
- scale 缩放
- rotate 旋转
- skew 错切
在学习绘制操作的时候很多地方都使用到了translate 和rotate 这里就不再对它们进行操作
1.画布scale
//画布缩放
private void canvasScale(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
int hWidth = width / 2;
int hHeight = height / 2;
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.STROKE);
//使用path 画布中心的X Y 轴
Path path = new Path();
path.moveTo(hWidth, 0);
path.lineTo(hWidth, height);
path.moveTo(0, hHeight);
path.lineTo(width, hHeight);
canvas.drawPath(path, mPaint);
//画布平移
canvas.translate(hWidth, hHeight);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
RectF rectF = new RectF(0, 0, 200, 100);
canvas.drawRect(rectF, mPaint);
//默认0,0进行缩放
canvas.scale(1.5f, 1.5f);
mPaint.setColor(Color.BLUE);
canvas.drawRect(rectF, mPaint);
//100,0进行缩放
canvas.scale(1.5f, 1.5f, 100, 0);
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);
/*
当缩放倍数为负数时,会先进行缩放,然后根据不同情况进行图形翻转:
(设缩放倍数为(a,b),旋转中心为(px,py)):
a<0,b>0:以px为轴翻转
a>0,b<0:以py为轴翻转
a<0,b<0:以旋转中心翻转
*/
//100,0进行缩放 以Y轴翻转
canvas.scale(1f, -1f, 100, 0);
mPaint.setColor(Color.BLACK);
canvas.drawRect(rectF, mPaint);
}
2.画布skew
//画布错切
private void canvasSkew(Canvas canvas) {
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
RectF rectF = new RectF(0, 0, 400, 200);
canvas.translate(100,100);
canvas.drawRect(rectF, mPaint);
canvas.save();
canvas.translate(0,300);
//画布X正方向错切45°
canvas.skew(1f, 0);
canvas.drawRect(rectF, mPaint);
canvas.restore();
canvas.save();
canvas.translate(0,600);
//画布Y负方向错切45°
canvas.skew(0, -1f);
canvas.drawRect(rectF, mPaint);
canvas.restore();
mPaint.setColor(Color.GREEN);
canvas.drawLine(0,400,600,400,mPaint);
}
十三 画布裁剪
- clipPath
- clipRect
- clipRegion(过时)
- getClipBounds
//画布裁剪
private void canvasClip(Canvas canvas) {
canvas.drawColor(Color.RED);
canvas.save();
//矩形裁剪
canvas.clipRect(0, 0, 400, 200);
canvas.drawColor(Color.BLUE);
canvas.restore();
canvas.save();
canvas.translate(20, 400);
Path path = new Path();
path.addRect(0, 0, 400, 200, Path.Direction.CCW);
canvas.drawPath(path, mPaint);
//路径裁剪
canvas.clipPath(path);
canvas.drawColor(Color.GREEN);
//获得裁剪边界
Rect bounds = canvas.getClipBounds();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
canvas.drawRect(bounds, mPaint);
canvas.restore();
}
这里针对Canvas的API进行了一些说明,希望对大家有所帮助。没有添加完整工程上来,但是每一个方法都是实际跑过的,只需要在onDraw中调用就可以看到效果了。