Android——Canvas切割出扇形表盘式进度
一、知识点
(1)Matrix数学原理
(2)shader渲染
(3)PathEffect之DashPathEffect
原创者:It一zhai男
博客地址:http://www.cnblogs.com/ityizhainan/p/6306748.html
二、效果图
(1)Matrix的数学原理[1]
在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类。Android中的Matrix是一个3 x 3的矩阵,其内容如下:
Matrix的对图像的处理可分为四类基本变换:
Translate 平移变换
Rotate 旋转变换
Scale 缩放变换
Skew 错切变换
本例用到的是旋转变换。
(2)shader渲染[2]
在Android中,提供了Shader类专门用来渲染图像以及一些几何图形。
Shader类包括了5个直接子类,分别为:BitmapShader、ComposeShader、LinearGradient、RadialGradient以及SweepGradient。其中,BitmapShader用于图像渲染;ComposeShader用于混合渲染;LinearGradient用于线性渲染;RadialGradient用于环形渲染;而SweepGradient则用于梯度渲染。
使用Shader类进行图像渲染时,首先需要构建Shader对象,然后通过Paint的setShader()方法来设置渲染对象,最后将这个Paint对象绘制到屏幕上即可。
有一点需要注意,使用不同的方式渲染图像时需要构建不同的对象。
SweepGradient扫描渲染
public SweepGradient(float cx,float cy,int[] colors,float[] position)
parameters:
float cx:渲染中心x坐标
float cy:渲染中心y坐标
int[] colors:围绕中心渲染的颜色数组,颜色数组里至少要有两种颜色
float[] position:在颜色数组里每种颜色的相对位置,取值范围0到1.0
(3)PathEffect之DashPathEffect[3]
DashPathEffect主要用于画虚线。构造函数,看注释,intervals必须大于大于2,phase是偏移量
DashPathEffect(new float[]{2,4,6,8},1)
的第一个数组参数依次是画个长度为2的实线,再画个长度为4的空白再画个长度为6的实线,再画个长度为8的虚线,宽度是由mPaint.setStrokeWidth(mHeight/10)设置。
第二个参数指定了绘制的虚线相对了起始地址(Path起点)的取余偏移(对路径总长度)。
new DashPathEffect(new float[] { 8, 10, 8, 10}, 0);
这时偏移为0,先绘制实线,再绘制透明。
new DashPathEffect(new float[] { 8, 10, 8, 10}, 8);
这时偏移为8,先绘制了透明,再绘制了实线.(实线被偏移过去了)
可是通过不断地递增/递减来改变phase的值,达到一个路径自身不断循环移动的动画效果。
三、主要代码
package com.example.yds.circleprogressbar; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathEffect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; /** * Created by yds on 2017/1/16. */ //@SuppressLint("DrawAllocation") public class SampleView extends View { private Paint mPaint; private float mRotate; private Matrix mMatrix; private Shader mShader; private int mWidth; private int mHeight; public SampleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public SampleView(Context context, AttributeSet attrs) { super(context, attrs); } public SampleView(Context context) { super(context); } public void getArc(Canvas canvas, float o_x, float o_y, float r, float startangel, float endangel, Paint paint){ RectF rect = new RectF(o_x - r, o_y - r, o_x + r, o_y + r); Path path = new Path(); //将画笔移动到指定点,与lineTo结合画线。可以理解成画线的起始点 path.moveTo(o_x,o_y); //最关键的是下面两句,划线终点,改点是画布切割线与图形的交点处 path.lineTo((float)(o_x+r*Math.cos(startangel*Math.PI/180)) , (float)(o_y+r*Math.sin(startangel*Math.PI/180))); path.lineTo((float)(o_x+r*Math.cos(endangel*Math.PI/180)) , (float)(o_y+r*Math.sin(endangel*Math.PI/180))); //将要绘制的图形添加小path中 path.addArc(rect, startangel, endangel-startangel); //将path切割出来,这里切割的是画布,并不是图形本身 canvas.clipPath(path); //画圆 canvas.drawCircle(o_x, o_y, r, paint); } @Override protected void onDraw(Canvas canvas) { /** * Set whether this view can receive the focus. *设置这个视图是否能够得到焦点 * Setting this to false will also ensure that this view is not focusable * in touch mode. *设置为false即使在触摸模式下也不能得到焦点 * @param focusable If true, this view can receive the focus. *设置为true,这个视图可以获得焦点 * @see #setFocusableInTouchMode(boolean) * @attr ref android.R.styleable#View_focusable * */ setFocusable(true); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMatrix = new Matrix(); //设置获取焦点是否在触摸模式下 setFocusableInTouchMode(true); // float x = 320; // float y = 200; /** * A subclass of Shader that draws a sweep gradient around a center point. *围绕一个中心点渲染绘制的Shader父类 * @param cx The x-coordinate of the center 中心点x坐标 * @param cy The y-coordinate of the center 中心点y坐标 * @param colors The colors to be distributed between around the center. * There must be at least 2 colors in the array. * 颜色分布在中心点周围,在这个数组中必须至少有2种颜色 * @param positions May be NULL. The relative position of * each corresponding color in the colors array, beginning * with 0 and ending with 1.0. If the values are not * monotonic, the drawing may produce unexpected results. * If positions is NULL, then the colors are automatically * spaced evenly. * 可能为空。在颜色数组里的每种颜色的相对位置,取值范围为0到1.0。 * 如果值不变,绘画可能产生意想不到的结果。如果为空,那么颜色会自动均匀分布。 */ mShader = new SweepGradient(mWidth/2-1, mHeight/2-1, new int[] {0xFF09F68C, 0xFFB0F44B, 0xFFE8DD30, 0xFFF1CA2E, 0xFFFF902F, 0xFFFF6433}, null); mPaint.setShader(mShader); mPaint.setStyle(Paint.Style.STROKE); /**DashPathEffect(new float[]{2,4,6,8},1)的第一个数组参数依次是画个长度为2的实线,再画个长度为4的空白 * 再画个长度为6的实线,再画个长度为8的虚线,宽度是由mPaint.setStrokeWidth(mHeight/10)设置的 *第二个参数指定了绘制的虚线相对了起始地址(Path起点)的取余偏移(对路径总长度)。 * *new DashPathEffect(new float[] { 8, 10, 8, 10}, 0); *这时偏移为0,先绘制实线,再绘制透明。 * *new DashPathEffect(new float[] { 8, 10, 8, 10}, 8); *这时偏移为8,先绘制了透明,再绘制了实线.(实线被偏移过去了) * *可是通过不断地递增/递减来改变phase的值,达到一个路径自身不断循环移动的动画效果。 */ PathEffect effect = new DashPathEffect(new float[] { 10, 10, 10,10}, 0); //给画笔设置绘制路径时的特效 mPaint.setPathEffect(effect); //设置线条宽度 mPaint.setStrokeWidth(mHeight/10); Paint paint = mPaint; //// float x = 320; //// float y = 200; // canvas.drawColor(Color.WHITE); //setRotate(mRotate,mx,my)旋转,mRotate旋转角度,mx,my为旋转中心坐标。 mMatrix.setRotate(mRotate,mWidth/2-1, mHeight/2-1); mShader.setLocalMatrix(mMatrix); mRotate += 3; if (mRotate >= 405) { mRotate = 135; } invalidate(); // RectF rect = new RectF( mWidth/20, mWidth/20,mWidth-mWidth/20,mHeight-mWidth/20); // canvas.drawArc(rect,135,270,false,paint); getArc(canvas,mWidth/2-1, mHeight/2-1,mHeight/2-1,135,405,paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode==MeasureSpec.EXACTLY||heightSpecMode == MeasureSpec.EXACTLY){ mWidth = widthSpecSize;//这里的值为实际的值的3倍 mHeight =heightSpecSize; }else{ float defaultSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,200,getContext().getResources().getDisplayMetrics()); mHeight = (int) defaultSize; mWidth = (int) defaultSize; } if(mWidth<mHeight){ mHeight = mWidth; }else{ mWidth = mHeight; } // Log.e("mHeight,mWidth",mHeight+","+mWidth); setMeasuredDimension(mWidth,mHeight); } }
代码下载:
http://download.csdn.net/detail/u013293125/9741449
参考内容:
[1] http://www.cnblogs.com/qiengo/archive/2012/06/30/2570874.html#code
[2] http://www.cnblogs.com/menlsh/archive/2012/12/09/2810372.html
[3] http://blog.csdn.net/abcdef314159/article/details/51777353