代码改变世界

Android——Canvas切割出扇形表盘式进度

2017-01-19 15:16  It一zhai男  阅读(3638)  评论(0)    收藏  举报

 

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