文章目录
- Canvas 画布类
- 画布背景
- 点
- 线
- 矩形
- 椭圆
- 圆
- 弧形
- 路径
- 字符
- 对画布裁剪及变形
- Paint 画笔类
- 常用方法
- 图形线条相关
- 字符相关
- Path设置样式
如果是一个自定义控件,则需要派生自 View 或其子类,如果是一个自定义的容器,则需要派生自 ViewGroup 或其子类
Canvas 画布类
凡是要画出成品的东西,比如圆形、矩形、文字等,都调用 Canvas 类里的函数生成
画布背景
drawColor(int color); //必须是8位16进制:OxAARRGGBB,否则无作用
drawRGB(int r, int g, int b); //可10进制 0~255,或16进制 OxOO~OxFF
drawARGB(int a, int r, int g, int b);
点
// (x,y)绘制点的坐标
drawPoint(float x, float y, Paint);
drawPoints(float[] pts, Paint); //两个一组组成坐标,pts数组长度必须是二的倍数,例如[x0,y0, x1,y1, x2,y2 …]
//offset 在pts数组中取数值的偏移量,第一个数值为pts[offset]
//count 在pts数组总共取多少个数值,offset+count<=pts.length
drawPoints(float[] pts, int offset, int count, Paint); // 即切割数组,从pts[offset],到pts[offset+count-1],count必须是2的倍数
线
// (startX,startY)起始点坐标,(endX,endY)终点坐标
drawLine(float startX, float startY, float endX, float endY, Paint);
drawLines(float[] pts, Paint); //四个一组组成坐标,pts数组长度必须是四的倍数,例如[startX0,startY0,endX0,endY0, startX1,startY1,endX1,endY1 …]
drawLines(float[] pts, int offset, int count, Paint); // 同上,count必须是四的倍数
矩形
drawRect(float left, float top, float right, float bottom, Paint); //可以理解为矩形左上角坐标(left,top) 和右下角坐标(right,bottom)
drawRect(Rect rect, Paint);
drawRect(RectF rectF, Paint);
// 绘制圆角矩形,rx是横向,ry是纵向,单位px,数值越大角越圆
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint);
drawRoundRect(RectF rectF, float rx, float ry, Paint);
// 构造方法一:
Rect rect = new Rect(10, 10, 100, 100);
// 构造方法二:
Rect rect = new Rect();
rect.set(10, 10, 100, 100) ;
// 构造方法三:
Rect r = new Rect(10, 10, 100, 100);
Rect rect = new Rect(r);
// RectF 类与上面相同,只是精度不同,精度为float
椭圆
drawOval(float left, float top, float right, float bottom, Paint); //可以理解为一个矩形,在矩形之中画一个椭圆
drawOval(RectF rect, Paint);
圆
// cx和cy是圆的中心,radius是圆的半径
drawCircle(float cx, float cy, float radius, Paint)
弧形
// startAngle是圆弧开始角度,sweepAngle是圆弧经过的角度,useCenter设置圆弧是否经过中心
// 可以理解为只画出部分椭圆
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint);
drawArc(RectF, float startAngle, float sweepAngle, boolean useCenter, Paint);
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f;
canvas?.drawArc(10f, 10f, 200f, 100f, 0f, 90f, true, paint) //经过圆心
canvas?.drawArc(10f, 150f, 200f, 240f, 45f, 90f, false, paint) //不经过圆心
paint.style = Paint.Style.FILL
canvas?.drawArc(200f, 10f, 390f, 100f, 0f, 90f, true, paint) //经过圆心
canvas?.drawArc(200f, 150f, 390f, 240f, 45f, 90f, false, paint) //不经过圆心
}
路径
drawPath(Path path, Paint)
Path 类
1.常用方法
moveTo(float xl , float yl) //移动画笔到画布对应的坐标
rMoveTo(float dx , float dy) //相对于上一个画笔的最后一点,移动画笔,等价于上一个画笔位置的偏移量,如果没有上一个画笔位置,则将其视为moveTo()
lineTo(float x, float y) //从当前画笔位置,绘制一条直线到画布对应坐标(x,y)
rLineTo(float dx, float dy) //与lineTo相同,相对于当前画笔位置,等价于当前画笔位置为(x0,y0),lineTo(x0+dx, y0+dy)
arcTo(RectF oval, float startAngle, float sweepAngle) //绘制弧线,参数与画弧线相同
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) // forceMoveTo 是否强制地将弧的起始点作为绘制起始
arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) //与上面一个方法相同
close(); // 使用 close() 封闭子图形
//Path.Direction.CW 顺时针 Path.Direction.CCW 逆时针
addPath() //添加另一个路径
addRect() //添加一个矩形
addRoundRect() //添加一个圆角矩形
addCircle() //添加一个圆形
addOval() //添加一个椭圆
addArc() //添加一个弧线
quadTo(float x1, float y1, float x2, float y2) //画二次贝塞尔曲线,两个点的坐标值(x1,y1) (x2,y2)
rQuadTo(float dx1, float dy1, float dx2, float dy2) //相对坐标
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) //画三次贝塞尔曲线
rCubicTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) //相对坐标
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10f;
var path: Path = Path()
path.moveTo(10f, 10f)
// forceMoveTo = true
path.addArc(30f, 30f, 200f, 200f, -225f, 225f);
path.arcTo(400f, 200f, 600f, 400f, -180f, 225f, true);
// forceMoveTo = false
path.addArc(30f, 230f, 200f, 400f, -225f, 225f);
path.arcTo(400f, 400f, 600f, 600f, -180f, 225f, false);
canvas?.drawPath(path,paint)
}
2.FillType属性
setFillType(Path.FillType);
有四种类型 Path.FillType.WINDING、 Path.FillType.EVEN_ODD、Path.FillType.INVERSE_WINDING、Path.FillType.INVERSE_EVEN_ODD
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.FILL
paint.strokeWidth = 1f;
//Path.Direction.CW 顺时针 Path.Direction.CCW 逆时针
var path: Path = Path()
path.addCircle(200f, 200f, 100f, Path.Direction.CW) //方向相同
path.addCircle(300f, 200f, 100f, Path.Direction.CW)
path.fillType = Path.FillType.WINDING
canvas?.drawPath(path, paint)
var path2: Path = Path()
path2.addCircle(200f, 460f, 100f, Path.Direction.CW) //方向不同
path2.addCircle(300f, 460f, 100f, Path.Direction.CCW)
path2.fillType = Path.FillType.WINDING
canvas?.drawPath(path2, paint)
}
2.op方法
op(Path path1, Path path2, Op op)
/** Op 的值有
Path.Op.DIFFERENCE path1 减去 path2 的区域
Path.Op.INTERSECT 留下 path2 和 path2 相交的区域
Path.Op.UNION path1 和 path2 的并集
Path.Op.XOR 包含 path1 和 path2 但不包含相交的部分
Path.Op.REVERSE_DIFFERENCE path2 减去 path1 的区域
**/
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.FILL
paint.strokeWidth = 1f;
var path: Path = Path()
path.addCircle(200f, 200f, 100f, Path.Direction.CW)
var path2: Path = Path()
path2.addCircle(300f, 200f, 100f, Path.Direction.CW)
path.op(path2, Path.Op.REVERSE_DIFFERENCE)
canvas?.drawPath(path, paint)
// canvas?.drawPath(path2, paint) //注意不要在绘制path2了,op方法已经将path2的图像合并到path中了
}
字符
drawText(String text, float x, float y, Paint);
drawText(String text, int start, int end, float x, float y, Paint); //只画出字符串text的部分text[start]~text[end]
drawText(CharSequence text, int start, int end, float x, float y, Paint);//同上
drawText(char[] text, int index, int count, float x, float y, Paint); //同样之画出部分,count不是结束位置,而是取出多少个字符index+count<=text.length
// hOffset与路径起始点的水平偏移距离,vOffset与路径中心点的垂直偏移量
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint);
drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint);
注意:x的位置跟文字对齐方式有关(通过设置画笔的 setTextAlign() 方法指定,默认为left)
y的位置是基准线Baseline
通过这些来获取这些线的 y 的值
Paint.FontMetrics fontMetrics=paint.getFontMetrics();
// 注意:获取的值都是相对于Baseline的偏移量
fontMetrics.top //注意是负数,是相对于Baseline的偏移量
fontMetrics.ascent
fontMetrics.descent
fontMetrics.bottom
由上图清晰得出,基线相对于文字中心点的偏移量为:
//偏移量
float distance = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.FILL
paint.strokeWidth = 1f;
paint.textSize = 36f
var path: Path = Path()
path.moveTo(10f, 10f)
path.lineTo(300f, 300f)
canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path, 0f ,0f ,paint)
canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path, 110f ,60f ,paint)
paint.color = Color.YELLOW
paint.style = Paint.Style.STROKE
paint.strokeWidth = 1f;
canvas?.drawPath(path ,paint) //画出路径
var path1: Path = Path()
path1.arcTo(400f, 400f, 600f, 600f, 180f, 180f, false);
canvas?.drawPath(path1 ,paint) //画出路径
paint.color = Color.RED
paint.style = Paint.Style.FILL
canvas?.drawTextOnPath("Hello zwt!Hello zwt!", path1, 0f ,-60f ,paint)
}
对画布裁剪及变形
save() //将当前Canvas状态进行保存,会有一个int类型返回值
restore() //将Canvas还原成最近的一个save()的状态
restoreToCount(int saveCount) //将Canvas还原成某一个特定的save()状态, saveCount为调用save()所返回的数值
//平移
translate(float dx, float dy)
//旋转
rotate(float degrees) //以(0, 0)为原点
rotate(float degrees, float px, float py) //以(px, py)为原点
//斜切
skew(float sx, float sy) //sx,sy倾斜角度的tan值
//缩放
scale(float sx, float sy)
scale(float sx, float sy, float px, float py)
//裁剪
clipPath(Path path) // DIFFERENCE
clipPath(Path path, Region.Op op) // 已弃用,功能与Path的op方法相同
clipOutPath(Path path) //INTERSECT
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.FILL
paint.strokeWidth = 1f;
/*****************平移********************/
canvas?.drawRect(10f,10f, 500f, 300f, paint)
canvas?.translate(100f, 100f)
paint.setARGB(180, 255, 255, 0) //设置为黄色透明
canvas?.drawRect(10f,10f, 500f, 300f, paint)
/*****************旋转********************/
// canvas?.save() // 保存画布状态
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
// canvas?.rotate(45f) // 以(0,0)点旋转45°
// paint.setARGB(180, 255, 255, 0)
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
// canvas?.restore() //恢复画布状态
// canvas?.rotate(90f, 250f, 150f) // 以(250,150)点旋转90°
// paint.setARGB(180, 255, 0, 255)
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
/*****************斜切********************/
// canvas?.save() // 保存画布状态
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
// canvas?.skew(1f, 0f) // tan45° = 1,即斜切45°
// paint.setARGB(180, 255, 255, 0)
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
// canvas?.restore() //恢复画布状态
// canvas?.skew(0f, 1f)
// paint.setARGB(180, 255, 0, 255)
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
/*****************缩放********************/
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
//
// canvas?.scale(0.5f, 2f); // 宽度变为原来的一半,高度变为两倍
// paint.setARGB(180, 255, 255, 0)
// canvas?.drawRect(10f,10f, 500f, 300f, paint)
}
Paint 画笔类
凡是跟画笔相关的设置,比如画笔的大小、粗细、颜色、透明度、字体样式等
常用方法
setColor(int):设置画笔的颜色
setAlpha(int):设置画笔的透明度
setARGB(int a, int r, int g, int b):设置画笔的颜色,a代表透明度,r,g,b代表颜色值
setStrokeWidth(int):设置画笔的线宽
setAntiAlias(boolean):设置是否使用抗锯齿功能,设置后会平滑一些,但绘制速度会变慢
setDither(boolean):设定是否使用图像抖动处理,设置后图像更加清晰,但绘制速度会变慢
setStyle(Style):设置画笔的风格,Style.FILL 实心 Style.STROKE 空心 Style.FILL_AND_STROKE 同时显示实心和空心
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var paint: Paint = Paint()
paint.setColor(Color.RED)
paint.strokeWidth = 50f;
paint.style = Paint.Style.STROKE
canvas?.drawCircle(150f, 300f, 75f, paint);
paint.style = Paint.Style.FILL
canvas?.drawCircle(350f, 300f, 75f, paint);
paint.style = Paint.Style.FILL_AND_STROKE
canvas?.drawCircle(550f, 300f, 75f, paint);
paint.style = Paint.Style.FILL
canvas?.drawCircle(800f, 300f, 75f, paint)
paint.setARGB(128, 39, 117, 172) //约50%的透明度
paint.style = Paint.Style.STROKE
canvas?.drawCircle(800f, 300f, 75f, paint)
}
图形线条相关
setStrokeCap(Cap):设置画笔的线帽Paint.Cap.BUTT无线帽、Paint.Join.ROUND圆形、Paint.Join.BEVEL方形
setStrokeJoin(Join):设置连接Paint.Join.MITER锐角、Paint.Join.ROUND圆弧、Paint.Join.BEVEL直线
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 50f;
paint.setStrokeCap(Paint.Cap.BUTT);
canvas?.drawLine(100f, 100f, 500f, 100f, paint);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas?.drawLine(100f, 200f, 500f, 200f, paint);
paint.setStrokeCap(Paint.Cap.SQUARE);
canvas?.drawLine(100f, 300f, 500f, 300f, paint);
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 50f;
var path: Path = Path()
path.moveTo(100f,100f)
path.rLineTo(500f,0f)
path.rLineTo(-200f,200f)
paint.strokeJoin = Paint.Join.MITER
// paint.strokeJoin = Paint.Join.ROUND
// paint.strokeJoin = Paint.Join.BEVEL
canvas?.drawPath(path, paint)
}
字符相关
setTextSize(int):设置字体大小
setFakeBoldText(boolean):设置文本仿粗体
setUnderlineText(boolean):设置文字的下划线
setTextSkewX(float):设置斜体字,值为负右倾值为正左倾
setStrikeThruText(boolean):设置文本删除线
setTextScaleX(float):文本沿X轴水平缩放,默认值为1
setLetterSpacing(float):设置行的间距
setShadowLayer(float radius, float dx, float dy, int shadowColor):设置阴影效果,radius为阴影角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
setTypeface(Typeface):设置文本字体样式
setTextAlign(Paint.Align):设置字体方向,对其方式,Paint.Align.LEFT、Paint.Align.RIGHT、Paint.Align.CENTER
Path设置样式
setPathEffect(PathEffect):设置Path样式
CornerPathEffect(float radius):圆滑,radius转折角度,越大越圆滑
DashPathEffect(float intervals[], float phase):虚线:intervals[]指定虚实线长度,phase指定偏移量
PathDashPathEffect(Path shape, float advance, float phase, Style style):自定义效果,shape定义路径填充的样式,advance是每个图形之间的间距,phase指定偏移量,style分为ROTATE、MORPH和TRANSLATE
DiscretePathEffect(float segmentLength, float deviation):打散效果,segmentLength指定最大的段长,deviation则为绘制时的偏离量
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint: Paint = Paint()
paint.setColor(Color.RED)
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f;
var mPhase: Float = 5f //偏移量
var path: Path = Path()
path.moveTo(100f,100f)
path.rLineTo(100f,100f)
path.rLineTo(200f,-50f)
path.rLineTo(50f,20f)
path.rLineTo(100f,-60f)
path.rLineTo(100f,80f)
paint.pathEffect = null
canvas?.drawPath(path, paint)
var path1: Path = Path()
path1.moveTo(100f,200f)
path1.rLineTo(100f,100f)
path1.rLineTo(200f,-50f)
path1.rLineTo(50f,20f)
path1.rLineTo(100f,-60f)
path1.rLineTo(100f,80f)
paint.pathEffect = CornerPathEffect(25f) //圆滑
canvas?.drawPath(path1, paint)
var path2: Path = Path()
path2.moveTo(100f,300f)
path2.rLineTo(100f,100f)
path2.rLineTo(200f,-50f)
path2.rLineTo(50f,20f)
path2.rLineTo(100f,-60f)
path2.rLineTo(100f,80f)
paint.pathEffect = DashPathEffect(floatArrayOf(20f, 5f, 10f, 5f), mPhase) //圆滑
canvas?.drawPath(path2, paint)
var path3: Path = Path()
path3.moveTo(100f,400f)
path3.rLineTo(100f,100f)
path3.rLineTo(200f,-50f)
path3.rLineTo(50f,20f)
path3.rLineTo(100f,-60f)
path3.rLineTo(100f,80f)
var shape: Path = Path()
shape.addCircle(0f, 0f, 3f, Path.Direction.CW);
paint.pathEffect = PathDashPathEffect(shape, 12f, mPhase, PathDashPathEffect.Style.ROTATE)
canvas?.drawPath(path3, paint)
var path4: Path = Path()
path4.moveTo(100f,500f)
path4.rLineTo(100f,100f)
path4.rLineTo(200f,-50f)
path4.rLineTo(50f,20f)
path4.rLineTo(100f,-60f)
path4.rLineTo(100f,80f)
paint.pathEffect = DiscretePathEffect(5.0f, mPhase)
canvas?.drawPath(path4, paint)
}