目录

  • 前言
  • 一、GradientProgressView准备工作
  • 二、制作
  • 1.初始化特点
  • 2.丈量宽高
  • 3.依据状况来画突变色进展
  • 4.制作
  • 总结

前言

我们常运用shape完成突变色,可是shape的极限却只有三色,如果有超过三种色彩的View的要求,那么我们就不得不去自界说View来完成这个需求。不过它的难度也是很低的,完成起来很轻松。接下来我分析一下我的思路。 先上图:

一、GradientProgressView准备工作

  1. color:突变进展条的多种色彩
  2. 图形:如果是圆柱形就运用RectF,矩形就用Rect,一个用于显现原色进展,一个用于显现突变进展条
  3. 突变染色器:LinearGradient
  4. 宽高:mHeight、mWidth
  5. 圆柱的半径:mRadius 特点代码如下
    private int colorStart; // 色彩:进展1
    private int colorEnd; // 色彩:进展5
    private int colorStart2;// 色彩:进展2
    private int colorEnd0;// 色彩:进展4
    private int colorCenter;// 色彩:进展3
    private Paint mGradientPaint;// 突变画笔
    private Paint mPaint;// 默许画笔
    private RectF mBackGroundRect; // 原色图形
    private RectF mGradientRect;// 染色图形
    private LinearGradient backGradient;// 突变色
    private int mHeight;
    private int mWidth;
    private int mRadius;

二、制作

1.初始化特点

由于这个自界说View只为了满意公司需求,所有我并未将其的功能扩展开来,所有没有自界说特点,都是写死在代码中,后续有空再优化。 首要三过程:初始化色彩,初始化画笔,初始化图形。 最好先写死需求的宽高,防止在运用的运用还未丈量就进行制作

    private void init(Context context, @Nullable AttributeSet attrs){
        // 初始化画笔
        mGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        colorStart = Color.parseColor("#63DC80");
        colorStart2 = Color.parseColor("#A8DB5B");
        colorCenter = Color.parseColor("#FFCE48");
        colorEnd0 = Color.parseColor("#FE7B39");
        colorEnd = Color.parseColor("#E8402B");
        mPaint.setColor(Color.parseColor("#EDEDED"));
        if (attrs!=null){
        }
        //设置抗锯齿
        mGradientPaint.setAntiAlias(true);
        //设置防抖动
        mGradientPaint.setDither(true);
        mGradientPaint.setStyle(Paint.Style.FILL);
        //设置抗锯齿
        mPaint.setAntiAlias(true);
        //设置防抖动
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.FILL);
        mBackGroundRect = new RectF();
        // 防止烘托还未完成果显现,使得宽高还未丈量。先在初始化这儿界说好跟xml中相同的宽高
        mWidth = (int) (Resources.getSystem().getDisplayMetrics().widthPixels-context.getResources().getDimension(R.dimen.base_dp_56));
        mHeight = (int) (context.getResources().getDimension(R.dimen.base_dp_8));
    }

2.丈量宽高

这一步一般来说已经是有规范的代码模板了,所以我这儿也没什么创新的当地,照抄就完事了,这儿便浅浅介绍一下丈量的过程吧。 其流程是:

  1. 先用getMode()和getSize()获取丈量形式和宽高,再依据不同的形式获取宽高。
  2. 形式有三种:EXACTLY(精确值形式)、AT_MOST(最大值形式)、UNSPECIFIED(未指定形式)
  3. 如果是EXACTLY(精确值形式),那么该特点就是确认的,即当时View的宽高
  4. 如果是AT_MOST(最大值形式),那么该特点就是父容器的宽高
  5. 如果是UNSPECIFIED(未指定形式),那么该特点要么是0,要么是EXACTLY(精确值形式)下当时view的特点。 用图来阐明,即为:
    【自定义View】关于多色进度条GradientProgressView的绘制
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 从供给的丈量标准中获取丈量形式和宽高值
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        Log.d("TAG", "onMeasure: "+widthMode);
        Log.d("TAG", "onMeasure: "+widthSize);
        if (widthMode == MeasureSpec.EXACTLY) {
            //丈量形式是EXACTLY,阐明view的宽度值是确认的
            mWidth = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            /**
             * 丈量形式是AT_MOST,阐明view的宽度值最大是父View能供给的巨细
             * 比方开发者设置wrap_content,那可能是希望填充父View
             */
            mWidth = Math.max(mWidth,widthSize);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            mHeight = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            mHeight = Math.min(mHeight,heightSize);
        }
        /**
         * 为了View能更好的展示,需求设置下宽高比
         */
        float times = mWidth / (float)mHeight;
        if (times < 2.5 ) {
            if (mHeight < DensityUtil.dip2px(getContext(),60)) {
                mHeight = DensityUtil.dip2px(getContext(),60);
            }
            mWidth = (int) (mHeight * 2.5);
        }
        mRadius = (int) getContext().getResources().getDimension(R.dimen.base_dp_20);
        //保存核算后的宽高
        setMeasuredDimension(mWidth,mHeight);
    }

3.依据状况来画突变色进展

这儿由于需求原因,我将本来的进展条五色平分进展,所以这儿用到已经丈量好的宽度特点mWidth 。 这儿的流程也很简略,扼要说下

  1. 暴露方法给外界调用
  2. 获取进展
  3. 依据进展来给突变染色器:LinearGradient初始化
  4. 一同给圆柱形RectF初始化宽高
  5. 最后调用invalidate()从头制作该View
 public void setAirType(String degree){
        int level = mWidth / 5;
        switch (degree){
            case "1":
                mGradientRect = new RectF(0,0,level,mHeight);
                // 由于突变色调集,需求色彩大于等于2,这儿取巧,设置相同色彩
                backGradient = new LinearGradient(0, 0, level, 0, new int[]{colorStart,colorStart}, null, Shader.TileMode.CLAMP);
                break;
            case "2":
                mGradientRect = new RectF(0,0,level*2,mHeight);
                backGradient = new LinearGradient(0, 0, level*2, 0, new int[]{colorStart, colorStart2}, null, Shader.TileMode.CLAMP);
                break;
            case "3":
                mGradientRect = new RectF(0,0,level*3,mHeight);
                backGradient = new LinearGradient(0, 0, level*3, 0, new int[]{colorStart, colorStart2,colorCenter}, null, Shader.TileMode.CLAMP);
                break;
            case "4":
                mGradientRect = new RectF(0,0,level*4,mHeight);
                backGradient = new LinearGradient(0, 0, level*4, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0}, null, Shader.TileMode.CLAMP);
                break;
            case "5":
                mGradientRect = new RectF(0,0,mWidth,mHeight);
                backGradient = new LinearGradient(0, 0, mWidth, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0,colorEnd}, null, Shader.TileMode.CLAMP);
                break;
        }
        invalidate();
    }

4.制作

这儿我选用最简略粗犷的方法,直接叠了两个圆柱进展条,一个是原色的进展条,一个是突变色的进展条,这儿我们可以自行优化,即在backGradient 特点初始化的时分进展调整等。

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawRoundRect(canvas);
    }
    private void drawRoundRect(Canvas canvas) {
        mBackGroundRect.left = 0;
        mBackGroundRect.top = 0;
        mBackGroundRect.bottom = mHeight;
        mBackGroundRect.right = mWidth;
        // 制作底色圆角矩形
        canvas.drawRoundRect(mBackGroundRect, mRadius, mRadius, mPaint);
        // 突变绘图
        mGradientPaint.setShader(backGradient);
        //制作布景 圆角矩形
        if (mGradientRect != null) {
            canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mGradientPaint);
        }
    }

完好代码贴上,便利各位友友运用

public class GradientProgressView extends View {
    private int colorStart; // 色彩:进展1
    private int colorEnd; // 色彩:进展5
    private int colorStart2;// 色彩:进展2
    private int colorEnd0;// 色彩:进展4
    private int colorCenter;// 色彩:进展3
    private List<Integer> colorList; // 色彩调集
    private List<Float> colorSize; // 色彩位置
    private Paint mGradientPaint;// 突变画笔
    private Paint mPaint;// 默许画笔
    private RectF mBackGroundRect; // 原色图形
    private RectF mGradientRect;// 染色图形
    private LinearGradient backGradient;// 突变色
    private int mHeight;
    private int mWidth;
    private int mRadius;
    public GradientProgressView(Context context) {
        super(context);
    }
    public GradientProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }
    public GradientProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    private void init(Context context, @Nullable AttributeSet attrs){
        // 初始化画笔
        mGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        colorStart = Color.parseColor("#63DC80");
        colorStart2 = Color.parseColor("#A8DB5B");
        colorCenter = Color.parseColor("#FFCE48");
        colorEnd0 = Color.parseColor("#FE7B39");
        colorEnd = Color.parseColor("#E8402B");
        mPaint.setColor(Color.parseColor("#EDEDED"));
        if (attrs!=null){
        }
        //设置抗锯齿
        mGradientPaint.setAntiAlias(true);
        //设置防抖动
        mGradientPaint.setDither(true);
        mGradientPaint.setStyle(Paint.Style.FILL);
        //设置抗锯齿
        mPaint.setAntiAlias(true);
        //设置防抖动
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.FILL);
        mBackGroundRect = new RectF();
        // 防止烘托还未完成果显现,使得宽高还未丈量。先在初始化这儿界说好跟xml中相同的宽高
        mWidth = (int) (Resources.getSystem().getDisplayMetrics().widthPixels-context.getResources().getDimension(R.dimen.base_dp_56));
        mHeight = (int) (context.getResources().getDimension(R.dimen.base_dp_8));
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /**
         * 从供给的丈量标准中获取丈量形式和宽高值
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        Log.d("TAG", "onMeasure: "+widthMode);
        Log.d("TAG", "onMeasure: "+widthSize);
        if (widthMode == MeasureSpec.EXACTLY) {
            //丈量形式是EXACTLY,阐明view的宽度值是确认的
            mWidth = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            /**
             * 丈量形式是AT_MOST,阐明view的宽度值最大是父View能供给的巨细
             * 比方开发者设置wrap_content,那可能是希望填充父View
             */
            mWidth = Math.max(mWidth,widthSize);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            mHeight = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            mHeight = Math.min(mHeight,heightSize);
        }
        /**
         * 为了View能更好的展示,需求设置下宽高比
         */
        float times = mWidth / (float)mHeight;
        if (times < 2.5 ) {
            if (mHeight < DensityUtil.dip2px(getContext(),60)) {
                mHeight = DensityUtil.dip2px(getContext(),60);
            }
            mWidth = (int) (mHeight * 2.5);
        }
        mRadius = (int) getContext().getResources().getDimension(R.dimen.base_dp_20);
        //保存核算后的宽高
        setMeasuredDimension(mWidth,mHeight);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
          mWidth = w;
          mHeight = h;
    }
    public void setAirType(String degree){
        int level = mWidth / 5;
        switch (degree){
            case "1":
                mGradientRect = new RectF(0,0,level,mHeight);
                // 由于突变色调集,需求色彩大于等于2,这儿取巧,设置相同色彩
                backGradient = new LinearGradient(0, 0, level, 0, new int[]{colorStart,colorStart}, null, Shader.TileMode.CLAMP);
                break;
            case "2":
                mGradientRect = new RectF(0,0,level*2,mHeight);
                backGradient = new LinearGradient(0, 0, level*2, 0, new int[]{colorStart, colorStart2}, null, Shader.TileMode.CLAMP);
                break;
            case "3":
                mGradientRect = new RectF(0,0,level*3,mHeight);
                backGradient = new LinearGradient(0, 0, level*3, 0, new int[]{colorStart, colorStart2,colorCenter}, null, Shader.TileMode.CLAMP);
                break;
            case "4":
                mGradientRect = new RectF(0,0,level*4,mHeight);
                backGradient = new LinearGradient(0, 0, level*4, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0}, null, Shader.TileMode.CLAMP);
                break;
            case "5":
                mGradientRect = new RectF(0,0,mWidth,mHeight);
                backGradient = new LinearGradient(0, 0, mWidth, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0,colorEnd}, null, Shader.TileMode.CLAMP);
                break;
        }
        invalidate();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawRoundRect(canvas);
    }
    private void drawRoundRect(Canvas canvas) {
        mBackGroundRect.left = 0;
        mBackGroundRect.top = 0;
        mBackGroundRect.bottom = mHeight;
        mBackGroundRect.right = mWidth;
        // 制作底色圆角矩形
        canvas.drawRoundRect(mBackGroundRect, mRadius, mRadius, mPaint);
        // 突变绘图
        mGradientPaint.setShader(backGradient);
        //制作布景 圆角矩形
        if (mGradientRect != null) {
            canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mGradientPaint);
        }
    }
}

xml调用

        <com.itaem.blviewtest.GradientProgressView
            android:id="@+id/gradientProgress"
            android:layout_width="match_parent"
            android:layout_height="@dimen/base_dp_8"
            android:layout_marginTop="12dp"
            android:layout_marginHorizontal="@dimen/base_dp_44"/>

总结

仍是由于时间问题,该自界说View仅仅单纯满意需求,并没有太大的扩展性功能,后续有时间我也行会出一篇后续将自界说View补上,完成一个功能性较为强壮的突变色进展条。 自界说View的魅力就是当你完成功能需求的时分的满意感,不亚于曾经做完一道数学大题。那种痛快和欣喜,老实说我有点爱上自界说View了。

当然如果有可以优化的点,我们记住分享在评论区,一同进步。