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


    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

  • 相关阅读:
    HDU1325 Is It A Tree? (并查集判断有向图是否是树)
    DRF 视图家族及路由层补充
    DRF 外键字段深度查询优化、ListSerializer辅助完成群改
    DRF的orm多表关系补充及serializer子序列化
    DRF序列化
    DRF的封装:APIView类及五大模块
    DRF简介/接口概念
    vue-axios插件、django-cors插件、及vue如何使用第三方前端样式库:element/jQuery/bootstrap
    Vue项目的创建、路由、及生命周期钩子
    Vue框架-组件的概念及使用
  • 原文地址:https://www.cnblogs.com/ityizhainan/p/6306748.html
Copyright © 2020-2023  润新知