自定义绘制的方式是重写绘制方法,其中最常用的是 onDraw()
绘制的关键是 Canvas 的使用
Canvas 的绘制类方法: drawXXX() (关键参数:Paint)
Canvas 的辅助类方法:范围裁切和几何变换
可以使用不同的绘制方法来控制遮盖关系
1、Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用Canvas.drawXXX() 是自定义绘制最基本的操作。掌握了这些方法,你才知道怎么绘制内容,例如怎么画圆、怎么画方、怎么画图像和文字。组合绘制这些内容,再配合上 Paint 的一些常见方法来对绘制内容的颜色和风格进行简单的配置,就能够应付大部分的绘制需求了。
2. Paint 的完全攻略
Paint 可以做的事,不只是设置颜色,也不只是实心空心、线条粗细、有没有阴影,它可以做的风格设置真的是非常多、非常细。例如:拐角要什么形状?开不开双线性过滤?加不加特效?等等
3.、Canvas 对绘制的辅助——范围裁切和几何变换。
范围裁切:
几何变换
4、使用不同的绘制方法来控制绘制顺序
控制绘制顺序解决的并不是「做不到」的问题,而是性能问题。同样的一种效果,你不用绘制顺序的控制往往也能做到,但需要用多个 View 甚至是多层View 才能拼凑出来,因此代价是 UI 的性能;而使用绘制顺序的控制的话,一个View 就全部搞定了。
提前创建好 Paint 对象,重写 onDraw() ,把绘制代码写在 onDraw() 里面,就是自定义绘制最基本的实现。
Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制一个圆
canvas.drawCircle(300, 300, 200, paint);
}
注意:别漏写了 super.onDraw()
1. Canvas 类下的所有 draw- 打头的方法,例如 drawCircle()和drawBitmap() 。
2. Paint 类的几个最常用的方法。具体是:
Paint.setStyle(Style style) 设置绘制模式
Paint.setColor(int color) 设置颜色
Paint.setStrokeWidth(float width) 设置线条宽度
Paint.setTextSize(float textSize) 设置文字大小
Paint.setAntiAlias(boolean aa) 设置抗锯齿开关
下面具体说下上面两个类:
Canvas.drawColor(@ColorInt int color) 颜色填充方法:在整个绘制区域统一涂上指定的颜色。
例如 drawColor(Color.BLACK) 会把整个区域染成纯黑色,覆盖掉原有内容;drawColor(Color.parse("#88880000") 会在原有的绘制效果上加一层半透明的红色遮罩。
类似的方法还有 drawRGB(int r, int g, int b) 和drawARGB(int a, int r, int g, int b) ,它们和 drawColor(color) 只是使用方式不同,作用都是一样的。
canvas.drawRGB(100, 200, 100);
canvas.drawARGB(100, 100, 200, 100);
这类颜色填充方法一般用于在绘制之前设置底色,或者在绘制之后为界面设置半透明蒙版。
drawCircle(float centerX, float centerY, floatradius, Paint paint) 画圆。
前两个参数 centerX centerY 是圆心的坐标,第三个参数 radius 是圆的半径,单位都是像素,它们共同构成了这个圆的基本信息(即用这几个信息可以构建出一个确定的圆);第四个参数 paint,它提供基本信息之外的所有风格信息,例如颜色、线条粗细、阴影等。
在 Android 里,每个 View 都有一个自己的坐标系,彼此之间是不影响的。这个坐标系的原点是 View 左上角的那个点;水平方
向是 x 轴,右正左负;竖直方向是 y 轴,下正上负(注意,是下正上负,不是上正下负,和上学时候学的坐标系方向不一样)。
所以一个 View 的坐标 (x, y) 处,指的就是相对它的左上角那个点的水平方向 x 像素、竖直方向 y 像素的点。例如,(300, 300) 指的就是左上角的点向右 300 、向下300 的位置; (100, -50) 指的就是左上角的点向右 100 、向上 50 的位置。也就是说,canvas.drawCircle(300, 300, 200, paint) 这行代码绘制出的圆,在 View 中的位置和尺寸应该是这样的:
圆心坐标和半径,这些都是圆的基本信息,也是它的独有信息。什么叫独有信息?就是只有它有,别人没有的信息。你画圆有圆心坐标和半径,画方有吗?画椭圆有吗?这就叫独有信息。独有信息都是直接作为参数写进 drawXXX() 方法里的(比如 drawCircle(centerX, centerY, radius, paint) 的前三个参数)。而除此之外,其他的都是公有信息。比如图形的颜色、空心实心这些,你不管是画圆还是画方都有可能用到的,这些信息则是统一放在 paint 参数里的。
Paint.setColor(int color)
paint.setColor(Color.RED); // 设置为红色
canvas.drawCircle(300, 300, 200, paint);
Paint.setStyle(Paint.Style style)
而如果你想画的不是实心圆,而是空心圆(或者叫环形),也可以使用paint.setStyle(Paint.Style.STROKE) 来把绘制模式改为画线模式。
paint.setStyle(Paint.Style.STROKE); // Style 修改为画线模式
canvas.drawCircle(300, 300, 200, paint);
setStyle(Style style) 这个方法设置的是绘制的 Style 。 Style 具体来说有三种: FILL , STROKE 和 FILL_AND_STROKE 。 FILL 是填充模式, STROKE 是画线模式(即勾边模式), FILL_AND_STROKE 是两种模式一并使用:既画线又填充。它的
默认值是 FILL ,填充模式。
Paint.setStrokeWidth(float width)
在 STROKE 和 FILL_AND_STROKE 下,还可以使用paint.setStrokeWidth(float width) 来设置线条的宽度:
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20); // 线条宽度为 20 像素
canvas.drawCircle(300, 300, 200, paint);
抗锯齿
在绘制的时候,往往需要开启抗锯齿来让图形和文字的边缘更加平滑。开启抗锯齿很简单,只要在 new Paint() 的时候加上一个 ANTI_ALIAS_FLAG 参数就行:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
另外,使用 Paint.setAntiAlias(boolean aa) 来动态开关抗锯齿。
drawRect(float left, float top,float right, float bottom, Paint paint) 画矩形
left , top , right , bottom 是矩形四条边的坐标。另外,它还有两个重载方法 drawRect(RectF rect, Paint paint) 和
drawRect(Rect rect, Paint paint) ,让你可以直接填写 RectF 或 Rect 对象来绘制矩形。
drawPoint(float x, float y, Paint paint) 画点
x 和 y 是点的坐标。点的大小可以通过 paint.setStrokeWidth(width) 来设置;点的形状可以通过 paint.setStrokeCap(cap) 来设置: ROUND 画出来是圆形的点, SQUARE 或 BUTT 画出来是方形的点。
drawPoints(float[] pts, int offset, int count,Paint paint) / drawPoints(float[] pts, Paintpaint) 画点(批量)
同样是画点,它和 drawPoint() 的区别是可以画多个点。 pts 这个数组是点的坐标,每两个成一对; offset 表示跳过数组的前几个数再开始记坐标; count 表示一共要绘制几个点。
float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100, 150, 50}
// 绘制四个点:(50, 50) (50, 100) (100, 50) (100, 100)
canvas.drawPoints(points, 2 /* 跳过两个数,即前两个 0 */,
8 /* 一共绘制 8 个数(4 个点)*/, paint);
drawOval(float left, float top, float right, float bottom, Paint paint) 画椭圆
只能绘制横着的或者竖着的椭圆,不能绘制斜的(斜的倒是也可以,但不是直接使用 drawOval() ,而是配合几何变换,后面会讲到)。 left , top , right , bottom是这个椭圆的左、上、右、下四个边界点的坐标。
paint.setStyle(Style.FILL);
canvas.drawOval(50, 50, 350, 200, paint);
paint.setStyle(Style.STROKE);
canvas.drawOval(400, 50, 700, 200, paint);
另外,它还有一个重载方法 drawOval(RectF rect, Paint paint) ,让你可以直接填写 RectF 来绘制椭圆。
drawLine(float startX, float startY, float stopX,float stopY, Paint paint) 画线
canvas.drawPoints(points, 2 /* 跳过两个数,即前两个 0 */,8 /* 一共绘制 8 个数(4 个点)*/, paint);startX , startY , stopX , stopY 分别是线的起点和终点坐标。
drawLines(float [] pts, int offset, int count,Paint paint) / drawLines(float [] pts, Paint paint) 画线(批量)
drawLines() 是 drawLine() 的复数版。
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) 画圆角矩形
float[] points = {20, 20, 120, 20, 70, 20, 70, 120, 20, 120, 120, 12canvas.drawLines(points, paint);
left , top , right , bottom 是四条边的坐标, rx 和 ry 是圆角的横向半径和纵向半径。
drawArc(float left, float top, float right,float bottom,float startAngle, float sweepAngle,boolean useCenter, Paint paint) 绘制弧形或扇形
drawArc() 是使用一个椭圆来描述弧形的。 left , top , right , bottom 描述的是这个弧形所在的椭圆; startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。
drawPath(Path path, Paint paint) 画自定义图形
drawPath(path) 这个方法是通过描述路径的方式来绘制图形的,它的 path 参数就是用来描述图形路径的对象。 path 的类型是 Path ,使用方法大概像下面这样:
public class PathView extends View {
Paint paint = new Paint();
Path path = new Path(); // 初始化 Path 对象
......
{
// 使用 path 对图形进行描述(这段描述代码不必看懂)
path.addArc(200, 200, 400, 400, -225, 225);
path.arcTo(400, 200, 600, 400, -180, 225, false);
path.lineTo(400, 542);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint); // 绘制出 path 描述的图形(心形)
}
}
Path 有两类方法,一类是直接描述路径的,另一类是辅助的设置或计算。
Path 方法第一类:直接描述路径。
这一类方法还可以细分为两组:添加子图形和画线(直线或曲线)
第一组: addXxx() ——添加子图形
addCircle(float x, float y, float radius, Direction dir) 添加圆
x , y , radius 这三个参数是圆的基本信息,最后一个参数 dir 是画圆的路径的方向。路径方向有两种:顺时针 ( CW clockwise) 和逆时针 ( CCW counter-clockwise) 。对于普通情况,这个参数填 CW 还是填 CCW 没有影响。它只是在需要填充图形( Paint.Style 为 FILL 或 FILL_AND_STROKE ) ,并且图形出现自相交时,用于判断填充范围的。
第二组: xxxTo() ——画线(直线或曲线)
这一组和第一组 addXxx() 方法的区别在于,第一组是添加的完整封闭图形(除了addPath() ),而这一组添加的只是一条线。
lineTo(float x, float y) / rLineTo(float x,float y) 画直线从当前位置向目标位置画一条直线, x 和 y 是目标位置的坐标。这两个方法的区别是, lineTo(x, y) 的参数是绝对坐标,而 rLineTo(x, y) 的参数是相对当前位置的相对坐标 (前缀 r 指的就是 relatively 「相对地」)。
Path 方法第二类:辅助的设置或计算
这类方法的使用场景比较少,只讲其中一个方法:
setFillType(FillType fillType) 。
Path.setFillType(Path.FillType ft) 设置填充方式
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 画 Bitmap
绘制 Bitmap 对象,也就是把这个 Bitmap 中的像素内容贴过来。其中 left 和top 是要把 bitmap 绘制到的位置坐标。
drawText(String text, float x, float y, Paint paint) 绘制文字
界面里所有的显示内容,都是绘制出来的,包括文字。 drawText() 这个方法就是用来绘制文字的。参数 text 是用来绘制的字符串, x 和 y 是绘制的起点坐标。
通过 Paint.setTextSize(textSize) ,可以设置文字的大小