本文是学习公众号 hencoder 中的自定义View部分的学习笔记。
具体参考http://hencoder.com/ui-1-1/ 写的非常详细
练习:画弧形,扇形,心形,直方图,饼图
drawArc() 是使用一个椭圆来描述弧形的。left, top, right, bottom 描述的是这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL); // 填充模式
// canvas.drawOval(200, 100, 800, 500,paint);
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint); // 绘制扇形
canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint); // 绘制弧形
paint.setStyle(Paint.Style.STROKE); // 画线模式
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint); // 绘制不封口的弧形
使用drawPath(Path path, Paint paint)
Path类中的各种方法Hencoder已经写的非常清楚
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
//画圆弧,圆弧的最左侧距离y轴距离200 最上距离x轴距离200,
//最下距离x轴400 最右距离y轴400
// 开始弧度-225 弧形扫过的角度225
path.addArc(200, 200, 400, 400, -225, 225);
//画圆弧,圆弧的最左侧距离y轴距离400 最上距离x轴距离200,
//最下距离x轴400 最右距离y轴600
//开始弧度-180 弧形扫过的角度225 保留移动痕迹
path.arcTo(400, 200, 600, 400, -180, 225, false);
path.lineTo(400, 542);//从当前位置画线到(400,542)
path.close();//闭合图形
canvas.drawPath(path,paint);
该图是对这句代码的解释,剩下的几句代码的解释注释中都已写的很清楚。
//画圆弧,圆弧的最左侧距离y轴距离200 最上距离x轴距离200,
// 最下距离x轴400 最右距离y轴400 开始弧度-225 弧形扫过的角度225
path.addArc(200, 200, 400, 400, -225, 225);
确定好坐标后,画坐标系,文字,矩形,如下图所示
Paint paint = new Paint();
//绘制坐标系
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
Path path = new Path();
path.moveTo(150,100);//移动起点到(150,100)
path.lineTo(150,500);//从(150,100)开始画线,画到(150,150)处
path.lineTo(900,500);
canvas.drawPath(path,paint);
//绘制文字
paint.setTextSize(22);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(1);
canvas.drawText("Froyo",200,520,paint);
canvas.drawText("GB",315,520,paint);
canvas.drawText("ICS",415,520,paint);
canvas.drawText("JB",515,520,paint);
canvas.drawText("KitKat",605,520,paint);
canvas.drawText("L",725,520,paint);
canvas.drawText("M",825,520,paint);
//绘制长方形
paint.setColor(getResources().getColor(R.color.green_light));
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(190,495,270,500,paint);
canvas.drawRect(290,485,370,500,paint);
canvas.drawRect(390,485,470,500,paint);
canvas.drawRect(490,350,570,500,paint);
canvas.drawRect(590,250,670,500,paint);
canvas.drawRect(690,180,770,500,paint);
canvas.drawRect(790,370,870,500,paint);
1.绘制扇形
分析如下图所示
此处使用的是canvas的drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 其中各参数的作用:left, top, right, bottom 描述的是这个弧形所在的椭圆;startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。
注意要计算好角度
Paint paint = new Paint();
Path path = new Path();
//红色区域
paint.setColor(getResources().getColor(R.color.red_light));
canvas.drawArc(200,150,558,500,-180,125,true,paint);
//圆心(385,325) 直径350
//黄色区域
paint.setColor(getResources().getColor(R.color.yellow_light));
//left, top, right, bottom 描述的是这个弧形所在的椭圆;startAngle 是弧形的起始角度
//sweepAngle 是弧形划过的角度;useCenter 表示是否连接到圆心
canvas.drawArc(210,157,568,507,-55,55,true,paint);
//紫色区域
paint.setColor(getResources().getColor(R.color.purple_light));
canvas.drawArc(210,157,568,507,3,8,true,paint);
//灰色区域
paint.setColor(getResources().getColor(R.color.grey_light));
canvas.drawArc(210,157,568,507,13,7,true,paint);
//青色区域
paint.setColor(getResources().getColor(R.color.cyan_light));
canvas.drawArc(210,157,568,507,22,55,true,paint);
//蓝色区域
paint.setColor(getResources().getColor(R.color.blue_light));
canvas.drawArc(210,157,568,507,79,98,true,paint);
2.绘制白色的指示线
白色指示线的终点在每个弧形中点的位置,计算出每个白线中点。
//画线
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
path.moveTo(170,180);
path.lineTo(240,180);
path.lineTo(250,200);
canvas.drawPath(path,paint);
//第二条线 (385,325)圆心
path.moveTo(680,250);
path.lineTo(580,250);
path.lineTo(550,270);
canvas.drawPath(path,paint);
//第三条线
path.moveTo(680,370);
path.lineTo(640,370);
path.lineTo(620,350);
path.lineTo(565,350);
canvas.drawPath(path,paint);
//第四条线
path.moveTo(680,400);
path.lineTo(640,400);
path.lineTo(620,380);
path.lineTo(563,380);
canvas.drawPath(path,paint);
//第五条线
path.moveTo(600,470);
path.lineTo(550,470);
path.lineTo(520,450);
canvas.drawPath(path,paint);
//第六条线
path.moveTo(170,470);
path.lineTo(240,470);
path.lineTo(250,450);
canvas.drawPath(path,paint);
//绘制文字
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(20);
canvas.drawText("Lollipop",90,185,paint);
canvas.drawText("Marshmallow",690,255,paint);
canvas.drawText("Gingerbread",690,375,paint);
canvas.drawText("Ice Cream Sandwich",690,405,paint);
canvas.drawText("Jelly Bean",610,475,paint);
canvas.drawText("KitKat",100,475,paint);
具体参考:http://hencoder.com/ui-1-2/ 写的十分详细
具体参考:http://hencoder.com/ui-1-3/ 也是写的十分详细
具体参考: http://hencoder.com/ui-1-4/ 知识点写的十分详细
1) clipRect() 在矩形范围内裁切
//加上save和restore 恢复到裁切之前的状态 不然后面都会裁切掉
canvas.save();
canvas.clipRect(left,top, left+300,top+200);
canvas.drawBitmap(bitmap, left, top, paint);
canvas.restore();
// 第一种: 1.画出path 圆
path1.addCircle(point1.x + 200,point1.y + 200,150, Path.Direction.CW);
//2.根据path裁切
canvas.save();
canvas.clipPath(path1);
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
//第二种:
/**
* FillType 有四个值:
* 1.EVEN_ODD: Specifies that "inside" is computed by an odd number of edge crossings.
* 2.INVERSE_EVEN_ODD:Same as EVEN_ODD, but draws outside of the path, rather than inside.
* 3.WINDING:Specifies that "inside" is computed by a non-zero sum of signed edge crossings.
* 4.INVERSE_WINDING:Same as WINDING, but draws outside of the path, rather than inside.
*/
path2.setFillType(Path.FillType.INVERSE_WINDING);
path2.addCircle(point2.x+ 200,point2.y + 200,150, Path.Direction.CCW);
canvas.save();
canvas.clipPath(path2);
canvas.drawBitmap(bitmap, point2.x, point2.y, paint);
canvas.restore();
上面Path的FillType,详细解释可见:
http://android.jobbole.com/83427/
几何变换的使用大概分为三类:
1. 使用 Canvas 来做常见的二维变换;
2.使用 Matrix 来做常见和不常见的二维变换;
3.使用 Camera 来做三维变换。
1) 使用 Canvas 来做常见的二维变换:
注意:1.canvas的几何变换可以叠加使用,但是叠加时,要倒着写,需要先实现的效果写在后面
2.在做几何变换之前,需要使用save保存状态,做之后使用restore恢复状态
1.1 Canvas.translate(float dx, float dy) 平移
canvas.save();
//向右移动200
canvas.translate(200,0);
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
canvas.save();
//向左移动100
canvas.translate(-100,0);
canvas.drawBitmap(bitmap, point2.x, point2.y, paint);
canvas.restore();
1.2 旋转Canvas.rotate(float degrees, float px, float py)
canvas.save();
//如果想要两个效果叠加使用 需要倒着写,比如说想要先旋转再移动 需要把移动写在旋转之前
canvas.rotate(45,point1.x + bitmapWidth/2,point1.y+ bitmapHeight/2);
canvas.translate(200,100);
//先移动 再绘制 先旋转 再绘制
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
1.3 缩放 Canvas.scale(float sx, float sy, float px, float py)
canvas.save();
//参数里的 sx sy 是横向和纵向的放缩倍数; px py 是放缩的轴心。
canvas.scale(1.3f,1.3f,point1.x + bitmapWidth/2 ,point1.y + bitmapHeight/2);
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
1.4 skew(float sx, float sy) 错切
//参数里的 sx 和 sy 是 x 方向和 y 方向的错切系数。
canvas.save();
canvas.skew(0, 0.5f);
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
1)平移
canvas.save();
matrix.reset();
matrix.postTranslate(-100, -100);
canvas.concat(matrix);
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
canvas.save();
matrix.reset();
matrix.postTranslate(200, 0);
canvas.concat(matrix);
canvas.drawBitmap(bitmap, point2.x, point2.y, paint);
canvas.restore();
把 Matrix 应用到 Canvas 有两个方法: Canvas.setMatrix(matrix) 和 Canvas.concat(matrix)。
1.Canvas.setMatrix(matrix):用 Matrix 直接替换 Canvas 当前的变换矩阵,即抛弃 Canvas 当前的变换,改用 Matrix 的变换(注:根据下面评论里以及我在微信公众号中收到的反馈,不同的系统中 setMatrix(matrix) 的行为可能不一致,所以还是尽量用 concat(matrix) 吧);
2.Canvas.concat(matrix):用 Canvas 当前的变换矩阵和 Matrix 相乘,即基于 Canvas 当前的变换,叠加上 Matrix 中的变换。