赞
踩
在笔者的《 View绘制流程—自定义View相关》的文章中,讲解到,Android中最常见的自定义步骤:
①自定义View属性;
②在View的构造方法中获得自定义的属性;
③重写onMeasure(); --> 并不是必须的,大部分的时候还需要覆写
④重写onDraw();
以上四步刚好对应了笔者的《自定义View精炼详解第(一)课:基础理论部分和简单小实现》中的描述:最常见也最有效的自定义方式就是继承现有的控件和布局实现。我们在前面的课程中已经讲解了自定义View的自定义属性部分的讲解,接下来本节中笔者将带领大家一起学习onDraw()方法的相关内容,并在最后实现一个示例。
(PS:本节内容较多)
(1)Canvas类的定义和作用:《Android群英传》中对于Canvas的描述为:当测量好了一个View之后,我们就可以简单地重写onDraw()方法,并在Canvas对象上来绘制所需要的图形,首先我们来了解一下利用西永2D绘图API所必须的使用到的Canvas对象。要想在Android的界面中绘制相应的图像,就必须在Canvas上进行绘制。Canvas就像是一个画板,使用Paint作为画笔就可以在上面作画了。
接着我们在看官方文档
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是onDraw()方法中的唯一参数,在绘图时需要明确四个核心的东西(basic components):
用什么工具画?
这个小问题很简单,我们需要用一支画笔(Paint)来绘图。
当然,我们可以选择不同颜色的笔,不同大小的笔。
把图画在哪里呢?
我们把图画在了Bitmap上,它保存了所绘图像的各个像素(pixel)。
也就是说Bitmap承载和呈现了画的各种图形。
画的内容?
根据自己的需求画圆,画直线,画路径。
怎么画?
调用canvas执行绘图操作。
比如,canvas.drawCircle(),canvas.drawLine(),canvas.drawPath()将我们需要的图像画出来。
(2)利用Canvas绘制基本图形
①创建画笔:要想绘制内容,首先需要先创建一个画笔,代码如下:
- // 1.创建一个画笔
- private Paint mPaint = new Paint();
-
- // 2.初始化画笔
- private void initPaint() {
- mPaint.setColor(Color.BLACK); //设置画笔颜色
- mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充
- mPaint.setStrokeWidth(10f); //设置画笔宽度为10px
- }
-
- // 3.在构造函数中初始化
- public SloopView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initPaint();
- }
②绘制颜色:绘制颜色是填充整个画布,常用于绘制底色。
canvas.drawColor(Color.BLUE); //绘制蓝色
③绘制点:可以绘制一个点,也可以绘制一组点,代码如下:
- canvas.drawPoint(200, 200, mPaint); //在坐标(200,200)位置绘制一个点
- canvas.drawPoints(new float[]{ //绘制一组点,坐标位置由float数组指定
- 500,500,
- 500,600,
- 500,700
- },mPaint);
④绘制直线:绘制直线需要两个点,初始点和结束点,同样绘制直线也可以绘制一条或者绘制一组:
- canvas.drawLine(300,300,500,600,mPaint); // 在坐标(300,300)(500,600)之间绘制一条直线
- canvas.drawLines(new float[]{ // 绘制一组线 每四数字(两个点的坐标)确定一条线
- 100,200,200,200,
- 100,300,200,300
- },mPaint);
⑤绘制矩形:确定确定一个矩形最少需要四个数据,就是对角线的两个点的坐标值,这里一般采用左上角和右下角的两个点的坐标。
关于绘制矩形,Canvas提供了三种重载方法,第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。 其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制,如下:
- // 第一种
- canvas.drawRect(100,100,800,400,mPaint);
-
- // 第二种
- Rect rect = new Rect(100,100,800,400);
- canvas.drawRect(rect,mPaint);
-
- // 第三种
- RectF rectF = new RectF(100,100,800,400);
- canvas.drawRect(rectF,mPaint);
⑥绘制圆角矩形:绘制圆角矩形也提供了两种重载方式,如下:
- // 第一种
- RectF rectF = new RectF(100,100,800,400);
- canvas.drawRoundRect(rectF,30,30,mPaint);
-
- // 第二种
- canvas.drawRoundRect(100,100,800,400,30,30,mPaint);
⑦绘制圆:绘制圆形也比较简单, 绘制圆形有四个参数,前两个是圆心坐标,第三个是半径,最后一个是画笔,如下:
canvas.drawCircle(500,500,400,mPaint); // 绘制一个圆心坐标在(500,500),半径为400 的圆。
- RectF rectF = new RectF(100,100,600,600);
- // 绘制背景矩形
- mPaint.setColor(Color.GRAY);
- canvas.drawRect(rectF,mPaint);
-
- // 绘制圆弧
- mPaint.setColor(Color.BLUE);
- canvas.drawArc(rectF,0,90,false,mPaint);
⑧绘制圆弧:绘制圆弧就比较神奇一点了,为了理解这个比较神奇的东西,我们先看一下它需要的几个参数:
- RectF rectF2 = new RectF(100,700,600,1200);
- // 绘制背景矩形
- mPaint.setColor(Color.GRAY);
- canvas.drawRect(rectF2,mPaint);
-
- // 绘制圆弧
- mPaint.setColor(Color.BLUE);
- canvas.drawArc(rectF2,0,90,true,mPaint);
————————————————————我是分隔线——————————————————————
⑨简要介绍Paint:看了上面这么多,相信有一部分人会产生一个疑问,如果我想绘制一个圆,只要边不要里面的颜色怎么办?很简单,绘制的基本形状由Canvas确定,但绘制出来的颜色,具体效果则由Paint确定。如果你注意到了的话,在一开始我们设置画笔样式的时候是这样的:mPaint.setStyle(Paint.Style.FILL); //设置画笔模式为填充。为了展示方便,容易看出效果,之前使用的模式一直为填充模式,实际上画笔有三种模式,如下:
STROKE //描边
FILL //填充
FILL_AND_STROKE //描边加填充
为了区分三者效果我们做如下实验:
- Paint paint = new Paint();
- paint.setColor(Color.BLUE);
- paint.setStrokeWidth(40); //为了实验效果明显,特地设置描边宽度非常大
-
- // 描边
- paint.setStyle(Paint.Style.STROKE);
- canvas.drawCircle(200,200,100,paint);
-
- // 填充
- paint.setStyle(Paint.Style.FILL);
- canvas.drawCircle(200,500,100,paint);
-
- // 描边加填充
- paint.setStyle(Paint.Style.FILL_AND_STROKE);
- canvas.drawCircle(200, 800, 100, paint);
————————————————————我是分隔线——————————————————————
上效果图:
上完整代码:
- public class PieView extends View {
- // 颜色表
- private int[] mColors = {0xFFCCFF00, 0xFF6495ED, 0xFFE32636, 0xFF800000, 0xFF808000, 0xFFFF8C69, 0xFF808080,
- 0xFFE6B800, 0xFF7CFC00};
- // 饼状图初始绘制角度
- private float mStartAngle = 0;
- // 数据
- private ArrayList<PieData> mData;
- // 宽高
- private int mWidth, mHeight;
- // 画笔
- private Paint mPaint = new Paint();
-
- public PieView(Context context) {
- this(context, null);
- }
-
- public PieView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setAntiAlias(true);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mWidth = w;
- mHeight = h;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (null == mData)
- return;
- float currentStartAngle = mStartAngle; // 当前起始角度
- canvas.translate(mWidth / 2, mHeight / 2); // 将画布坐标原点移动到中心位置
- float r = (float) (Math.min(mWidth, mHeight) / 2 * 0.8); // 饼状图半径
- RectF rect = new RectF(-r, -r, r, r); // 饼状图绘制区域
-
- for (int i = 0; i < mData.size(); i++) {
- PieData pie = mData.get(i);
- mPaint.setColor(pie.getColor());
- canvas.drawArc(rect, currentStartAngle, pie.getAngle(), true, mPaint);
- currentStartAngle += pie.getAngle();
- }
-
- }
-
- // 设置起始角度
- public void setStartAngle(int mStartAngle) {
- this.mStartAngle = mStartAngle;
- invalidate(); // 刷新
- }
-
- // 设置数据
- public void setData(ArrayList<PieData> mData) {
- this.mData = mData;
- initData(mData);
- invalidate(); // 刷新
- }
-
- // 初始化数据
- private void initData(ArrayList<PieData> mData) {
- if (null == mData || mData.size() == 0) // 数据有问题 直接返回
- return;
-
- float sumValue = 0;
- for (int i = 0; i < mData.size(); i++) {
- PieData pie = mData.get(i);
-
- sumValue += pie.getValue(); //计算数值和
-
- int j = i % mColors.length; //设置颜色
- pie.setColor(mColors[j]);
- }
-
- float sumAngle = 0;
- for (int i = 0; i < mData.size(); i++) {
- PieData pie = mData.get(i);
-
- float percentage = pie.getValue() / sumValue; // 百分比
- float angle = percentage * 360; // 对应的角度
-
- pie.setPercentage(percentage); // 记录百分比
- pie.setAngle(angle); // 记录角度大小
- sumAngle += angle;
-
- Log.i("angle", "" + pie.getAngle());
- }
- }
- }
至此关于自定义View的第三个阶段draw先写这么多吧。其实,在该阶段涉及到的东西非常多,我们这提到的仅仅是九牛一毛,也只是一些常用的基础性的东西。笔者也期望自己以后有时间和精力和大家一道深入地学习draw部分的相关知识和开发技能。
最后奉上一张重要的Draw属性常用表,开发中可以参考:
操作类型 | 相关API | 备注 |
绘制颜色 | drawColor, drawRGB, drawARGB | 使用单一颜色填充整个画布 |
绘制基本形状 | drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc | 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧 |
绘制图片 | drawBitmap, drawPicture | 绘制位图和图片 |
绘制文本 | drawText, drawPosText, drawTextOnPath | 依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字 |
绘制路径 | drawPath | 绘制路径,绘制贝塞尔曲线时也需要用到该函数 |
顶点操作 | drawVertices, drawBitmapMesh | 通过对顶点操作可以使图像形变,drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用 |
画布剪裁 | clipPath, clipRect | 设置画布的显示区域 |
画布快照 | save, restore, saveLayerXxx, restoreToCount, getSaveCount | 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数 |
画布变换 | translate, scale, rotate, skew | 依次为 位移、缩放、 旋转、错切 |
Matrix(矩阵) | getMatrix, setMatrix, concat | 实际上画布的位移,缩放等操作的都是图像矩阵Matrix, 只不过Matrix比较难以理解和使用,故封装了一些常用的方法。 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。