自定义绘制的4个级别
- Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用
- Paint 的完全攻略
- Canvas 对绘制的辅助——范围裁切和几何变换
- 使用不同的绘制方法来控制绘制顺序
小知识点
1.android中坐标系原点: View 左上角的那个点
2.X与Y轴: X:右正左负 Y:下正上负(不是数学学的上正下负)
onDraw()
这里不再给基础方法一一代码编写展示了。文章最后提供一个五角星、饼状图、雷达图的代码示例。
有兴趣的可以一个一个尝试下。
Paint基础
提供一些风格信息:例如:如颜色、线条粗细、阴影等。
- Paint.setStyle(Style style) 设置绘制模式(控制实心圆还是空心圆)
/**
* Paint.Style.STROKE 设置绘制模式为画线模式
* Paint.Style.FILL 是填充模式,
* Paint.Style.ILL_AND_STROKE 是两种模式一并使用:既画线又填充
* */
- Paint.setColor(int color) 设置颜色
- Paint.setStrokeWidth(float width) 设置线条宽度
- Paint.setTextSize(float textSize) 设置文字大小
- Paint.setAntiAlias(boolean aa) 设置抗锯齿
//直接在构造函数里加入Paint.ANTI_ALIAS_FLAG也是抗锯齿
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas
drawXXX()系列方法
- .drawColor 颜色填充
drawColor(Color.BLACK) //会把整个区域染成纯黑色,覆盖掉原有内容
drawColor(Color.parse("#88880000")) //会在原有的绘制效果上加一层半透明的红色遮罩。
还有drawRGB:使用方式不同,效果是一样的
drawRGB(100, 200, 100);
drawARGB(100, 100, 200, 100);
- drawLine / drawLines 画线
1. drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
2. drawLines(float[] pts, int offset, int count,Paint paint)
drawLines(float[] pts, Paint paint) //画线(批量)
// 绘制个五角星
float[] points1 = {
0,0,300,0,//0 ,0是起点坐标 300,0 目标点坐标 // 画第二条线时以上一次的结束点作为下一次的起点位置,以此类推
300,0,60,200,
60,200,150,-150,
150,-150,250,200 ,
250,200 ,0,0
};
canvas.drawLines(points1, paint);
- drawCircle 画圆
/**
*centerX、centerY 坐标轴
*radius: 半径
*Paint : 画笔
* 可以提供基本的风格信息。可看上面Panit基础
*/
drawCircle(float centerX, float centerY,float radius, Paint paint)
//代码示例:
canvas.drawCircle(300, 300, 100, paint);// 画圆
- drawRect 画矩形
1. drawRect(float left, float top, float right, float bottom, Paint paint) 画矩形
两个重载方法 :drawRect(RectF rect, Paint paint)
drawRect(Rect rect, Paint paint) ,让你可以直接填写 RectF 或 Rect 对象来绘制矩形。
// 绘圆角矩形
canvas.drawRoundRect(100,300, 300, 100,30,10, paint)
- drawPoint 画点
/* Paint.Cap.ROUND // 圆头
* Paint.Cap.SQUARE // 方头
* Paint.Cap.BUTT // 平头
* */
drawPoint(float x, float y, Paint paint) 画点
/**
* offset:开始绘制之前要跳过的值的数量
* count: 要处理的值的数目,在跳过它们的偏移量之后。从一个点
* 使用两个值,绘制的“点”的数量实际上是(计数>> 1)。
*/
drawPoints(float[] pts, int offset, int count,Paint paint)
drawPoints(float[] pts, Paint paint) 画点(批量)
//代码示例:
float[] points = {0, 0, 50, 50, 50, 100, 100, 50, 100, 100};
// 绘制四个点:(50, 50) (50, 100) (100, 50) (100, 100)
canvas.drawPoints(points, 2 /* 跳过两个数,即前两个 0 */, 8 /* 一共绘制 8 个数(4 个点)*/, paint);
- drawOval 画椭圆
drawOval(float left, float top, float right, float bottom, Paint paint) //画椭圆
重载方法:drawOval(RectF rect, Paint paint) //让你可以直接填写 RectF 来绘制椭圆。
- drawArc 画弧 / 画扇形
/*drawArc() 是使用一个椭圆来描述弧形的。left , top , right , bottom 描述的是
* 这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方
* 向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划
* 过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果
* 连接到圆心,就是扇形
* */
//再次提醒:sweepAngle : 开始绘制时起始角度 sweepAngle 是弧形划过的角度
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle,boolean useCenter, Paint paint)
- drawText
drawText(String text,float x, float y, Paint paint) 绘制文字
- drawBitmap
/**
* 绘制 Bitmap 对象,也就是把这个 Bitmap 中的像素内容贴过来。其中 left 和
* top 是要把 bitmap 绘制到的位置坐标。
*/
drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
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)
- drawPath 一般用于绘制复杂的图形
drawPath( Path path, Paint paint)
Path
小知识点:
- Direction 路径的方向
Path.Direction.CW 顺时针
Path.Direction.CCW 逆时针
- Path.FillTpe
Path.setFillTpe(Path.FillTpe ft) 设置填充
- Path使用:
// 最后使用时,调用drawPath 把path传入
canvas.drawPath( Path path, Paint paint)
Path.addXXX()系列 : 直接描述路径
- addCircle 添加圆
addCircle(float x, float y, float radius, Direction dir) 添加圆
- addOval 添加椭圆
addOval(float left,float top,float right,float bottom, Direction dir)
addOval(RectF oval, Direction dir)
- addRect 矩形
addRect(float left, float top, float right, float bottom, Path.Direction dir)
addRect(RectF rect, Path.Direction dir)
- addRoundRect 添加圆角矩形
addRoundRect(RectF rect, float[] radii, Path.Direction dir)
addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
- addPath 添加另一个 Path
addPath(Path path) 添加另一个 Path
和上面drawxxx用法都差不多。
xxxTo() ;
- lineTo (绝对坐标) 画线
- rLineTo (相对坐标)即当前位置,最后一次调用画 Path 的方法的终点位置。初始值为原点 (0, 0)
- moveTo 设置下一次绘制时的起点位置
lineTo :绝对坐标
rLineTo :相对坐标
//代码示例
paint.setStyle(Style.STROKE);
path.lineTo(200, 200); // 由当前位置 (0, 0) 向 (200, 200) 画一条直线
path.rLineTo(200, 0); // 由当前位置 (200,200) 向正右方 200 像素的位置画
path.lineTo(100,200);
path.moveTo(100,200); // 下次绘制时,起点就是(100,200)的位置
moveTo与rLineTo的区别:rLineTo 是以上一次的终点为原点位置,初始值为原点(0,0);
moveTo 设置下一次绘制时的起点位置
如下图:
path.lineTo(200, 200);
path.rLineTo(200, 0);
- quadTo / rQuadTo 画二次贝塞尔曲线 (还在研究中,看官自行略过 )
quadTo(float x1, float y1, float x2,float y2)
rQuadTo(float dx1, float dy1, float dx2,float dy2)
- cubicTo / rCubicTo 画三次贝塞尔曲线 (同上 )
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) /
rCubicTo(float x1, float y1, float x2,float y2, float x3,float y3)
- arcTo() 画弧形
arcTo(float left,float top,float right,float bottom,float startAngle,float sweepAngle,boolean forceMoveTo)
arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
arcTo(RectF oval, float startAngle, float sweepAngle)
//代码示例
Path path=new Path();
path.lineTo(100, 100);
path.arcTo(100, 100, 400, 400, -180, 180, true);
canvas.drawPath(path ,paint);
arcTo 和 drawArc() 比起来,只用来画弧形而不画扇形,所以不需要useCenter这个参数,多了一个forceMoveTo
forceMoveTo = true:
forceMoveTo = false:
看到区别没:
true:绘制是要 「抬一下笔移动过去」
false :「直接拖着笔过去」
二者的影响就是:是否留下移动的痕迹
- addArc() 画弧形
addArc(float left,float top,float right,float bottom, float startAngle,float sweepAngle)
addArc(RectF oval, float startAngle, float sweepAngle)
addArc()相对于 arcTo 只是一个直接使用了 forceMoveTo = true 的简化版
- close() 封闭当前子图形
当前位置向当前子图形的起点绘制一条直线。
Path的一些判断方法
- isEmpty
//path中是否包含内容
boolean b=path.isEmpty()
- isRect
//path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中
boolean b=path.isRect()
- set
将新的path赋值到现有path
path.set(path1); // 相当于 path=path1 ;
- offset
进行平移
offset(float dx,float dy)
offset(float dx,float dy,Path dst)
dst:用来存储平移后的path路径内容,无论dst之前是否存在内容,存储的都是当前的path路径内容
path.offset(300,0,dst);
五角星
WindowManager wm1 = mCtx.getWindowManager();
int width1 = wm1.getDefaultDisplay().getWidth();
int height1 = wm1.getDefaultDisplay().getHeight();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(5);
paint.setColor(Color.GRAY);
canvas.translate(width1/3, height1/3);// 移动原点位置
//五角星
Path path = new Path();
path.lineTo(300, 0);
path.lineTo(60, 200);
path.lineTo(150, -150);
path.lineTo(250, 200);
path.setFillType(Path.FillType.EVEN_ODD);
path.close();
//圆
Path pathCircle = new Path();
pathCircle.addCircle(350, 450, 50, Path.Direction.CCW);
pathCircle.addCircle(350, 450, 100, Path.Direction.CCW);
pathCircle.setFillType(Path.FillType.EVEN_ODD);
path.addPath(pathCircle);
canvas.drawPath(path, paint);
饼状图
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
centerX = w / 2;
centerY = h / 2;
radius = Math.min(w, h) / 2 * 0.8f;
Log.e("三角函数: ", Math.PI * 2 + " " + centerX + " radius=" + radius);
//一旦size发生改变,重新绘制
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
// 起始绘制度数
private float STARTANGLE = -180;
int radius = 200;// 初始化半径
int offset = 30;// 离原点的偏移距离( x y 轴的垂直距离)
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void drawPieChart(Canvas canvas) {
// 初始化Paint
Paint paint_A = new Paint();
paint_A.setColor(Color.RED);
Paint paint_B = new Paint();
paint_B.setColor(Color.GRAY);
Paint paint_C = new Paint();
paint_C.setColor(Color.GREEN);
Paint paint_D = new Paint();
paint_D.setColor(Color.LTGRAY);
//1.模拟数据
List<PieChartBean> mList = new ArrayList<>();
mList.add(new PieChartBean(200, "A"));
mList.add(new PieChartBean(100, "B"));
mList.add(new PieChartBean(200, "C"));
mList.add(new PieChartBean(300, "D"));
// 2.计算所有类型的总数值
float allNum = 0;
for (int i = 0; i < mList.size(); i++) {
allNum = allNum + mList.get(i).getNum();
}
// 3.求出每个Type占总的比例值,以此来分配所占的饼状图(圆形)的度数值
String type = "";
for (int i = 0; i < mList.size(); i++) {
if (i != 0) {
// 得出当前Type的值
//第一条数据无需获取,为后面当前type值时获取绘制的起始角度值使用
type = mList.get(i - 1).getType();
}
switch (mList.get(i).getType()) {
case "A":
canvas.drawArc(centerX - offset - radius, centerY - offset - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_A);
break;
case "B":
canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_B);
break;
case "C":
canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_C);
break;
case "D":
canvas.drawArc(centerX - radius, centerY - radius, centerX + radius, centerY + radius, getStartAngle(mList, type, allNum), getSweepAngle(mList.get(i).getNum(), allNum), true, paint_D);
break;
}
}
}
/**
* 画扇形扫过的度数
*/
private float getSweepAngle(float sweepAngle, float allNum) {
return (sweepAngle / allNum) * 360;
}
/**
* 获取每个Type开始绘制的起始度数
*/
private float getStartAngle(List<PieChartBean> mList, String type, float allNum) {
float startAngle = 0;// 记录当前Type的偏移度数
for (int i = 0; i < mList.size(); i++) {
startAngle = startAngle + (mList.get(i).getNum() / allNum) * 360;
if (mList.get(i).getType().equals(type)) {
//匹配到当前type度数值时,结束循环
break;
}
}
return STARTANGLE + startAngle;
}
雷达图
private int count = 8;
private float angle = (float) (Math.PI * 2 / count);//初始化正多边形 原点到每两点之间的夹角度数
private int centerX;
private int centerY;
private Activity mCtx;
private Paint paint;
private Paint linePaint;
private Paint drawRegionPaint;
private float radius;// 总半径 如下图原点到A点的距离
private float maxValue = 100;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
centerX = w / 2;
centerY = h / 2;
radius = Math.min(w, h) / 2 * 0.8f;
Log.e("三角函数: ", Math.PI * 2 + " " + centerX + " radius=" + radius);
//一旦size发生改变,重新绘制
postInvalidate();
super.onSizeChanged(w, h, oldw, oldh);
}
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(5);
paint.setColor(Color.GRAY);
paint.setStyle(Paint.Style.STROKE);
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setStrokeWidth(5);
linePaint.setColor(Color.GREEN);
linePaint.setStyle(Paint.Style.STROKE);
drawRegionPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
drawRegionPaint.setStyle(Paint.Style.FILL);
drawRegionPaint.setStrokeWidth(3);
drawRegionPaint.setColor(Color.RED);
regionList = new ArrayList<>();
regionList.add(100.0);
regionList.add(60.0);
regionList.add(20.0);
regionList.add(30.0);
regionList.add(70.0);
regionList.add(20.0);
regionList.add(40.0);
regionList.add(55.0);
}
/**
* 绘制最底层多边形
*/
private void drawSpiderWeb(Canvas canvas) {
Path pathPoint = new Path();
pathPoint.addCircle(centerX, centerY, 4, Path.Direction.CCW); // 绘制中心点的 圆点作为原点
Path path = new Path();
//蜘蛛网 按层级划分节点 从原点开始半径依次增大(增大相同距离),有几个节点就有几个半径
// 相邻两个节点的距离 =radius(蜘蛛网的总半径) / 分成了几个层级
// 如下图 原点到最外层一共7个正多边形 一共有7个节点层级
float r = radius / (count - 1);//得到平均每次增长的大小(相邻两个节点的距离)
// path.setFillType(Path.FillType.EVEN_ODD);
for (int i = 0; i < count; i++) {
path.reset();
float nextRadius = r * i;// 原点到当前节点的距离(当前层级的正多边形与原点的距离)
for (int j = 0; j < count; j++) {
if (j == 0) {
path.moveTo(centerX + nextRadius, centerY);// 设置绘制的起点
} else {
//对于直角三角形sin(x)是对边比斜边,cos(x)是底边比斜边,tan(x)是对边比底边
//因此可以推导出:底边(x坐标)=斜边(半径)*cos(夹角角度)
// 对边(y坐标)=斜边(半径)*sin(夹角角度)
float x = (float) (centerX + nextRadius * Math.cos(angle * j));
float y = (float) (centerY + nextRadius * Math.sin(angle * j));
// Log.e("三角函数: ", "x==" + x + " y==" + y + " centerX==" + centerX + " centerY==" + centerY);
path.lineTo(x, y);
}
}
path.close();
path.addPath(pathPoint);
canvas.drawPath(path, paint);
path.reset();
pathPoint.reset();
}
}
/**
* 从原点连接从里道外多边形顶点 形成蜘蛛网
*/
private void drawLines(Canvas canvas) {
Path path = new Path();
for (int i = 0; i < count; i++) {
path.reset();
path.moveTo(centerX, centerY);
float x = (float) (centerX + radius * Math.cos(angle * i));
float y = (float) (centerY + radius * Math.sin(angle * i));
path.lineTo(x, y);
canvas.drawPath(path, linePaint);
}
path.reset();
}
/**
* 绘制蜘蛛网上面的图
*/
private void drawRegion(Canvas canvas) {
Path regionPath = new Path();
for (int i = 0; i < count; i++) {
Double aDouble = regionList.get(i);
double percent = aDouble / maxValue;// 算出数据和最大半径的比值 //maxValue代表最大半径代表的值
double percentValue = percent * radius;
float x = (float) (centerX + percentValue * Math.cos(angle * i));
float y = (float) (centerY + percentValue * Math.sin(angle * i));
if (i == 0) { // 迁移绘制起点
regionPath.moveTo(x, y);
} else {
regionPath.lineTo(x, y);
}
canvas.drawCircle(x, y, 10, drawRegionPaint);
}
regionPath.close();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setAlpha(155);
canvas.drawPath(regionPath, paint);
}
/**
* 画文字
*/
private void drawToText(Canvas canvas) {
Paint textPaint = new TextPaint();
textPaint.setColor(Color.BLUE);
textPaint.setTextSize(30);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float fontHeight = fontMetrics.descent - fontMetrics.ascent;
//绘制文字时不让文字和雷达图形交叉,加大绘制半径
float textRadius = radius + fontHeight;
double pi = Math.PI;
List<String> titleList = new ArrayList<>();
titleList.add("A");
titleList.add("B");
titleList.add("C");
titleList.add("D");
titleList.add("E");
titleList.add("F");
titleList.add("G");
titleList.add("GF");
titleList.add("GGGG");
for (int i = 0; i < count; i++) {
float x = (float) (centerX + textRadius * Math.cos(angle * i));
float y = (float) (centerY + textRadius * Math.sin(angle * i));
canvas.drawText(titleList.get(i), x, y, textPaint);
}
}
自定义 基础绘制,笔记就做了这么多!各位看官如果又发现什么不对的,或者不完善的,请指出!谢谢