仿华为手机管家“一键优化”Loading加载框

仿华为手机管家“一键优化”Loading加载框

最近公司项目版本通过了没事做,闲来无聊学习下自定义view知识。偶尔看到华为手机上面的手机管家应用上面的loading图,于是想模仿一下,练练手~

废话不多说,先看下效果图

   

左边是华为自带应用的效果图,感觉挺漂亮的。右边是自己模仿的山寨版~

首先我们要看下华为这个加载框的静态图。
在这里插入图片描述

分析一下,首先我们可以将这个图形拆成几个部分。
1.最外面的圆圈和带渐变的内圆圈
2.刻度指针跟渐变的刻度指针
3.内部的进度值

化繁为简,首先我们先画出最外面的圆,代码就不用解释了

  canvas.drawCircle(mWidth/2,mHeight/2,radius,mPaint);

mWidth和mHeight是该View的宽和高,我们可以在onMeasure里面获取得到

    private int mHeight;
    private int mWidth;
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if(widthMode == MeasureSpec.EXACTLY){
            mWidth = widthSize;
        }else{
           //没定义具体大小的话,具体给200px宽高
            mWidth = 200;
        }

        if(heightMode == MeasureSpec.EXACTLY){
            mHeight = heightSize;
        }else{
            mHeight = 200;
        }

        setMeasuredDimension(mWidth,mHeight);
    }

画出来效果是这样的
在这里插入图片描述
怎么感觉怪怪的?? 仔细看了下效果图,发现它是有个渐变的过程,圆圈的颜色从上到下由深至浅~ 这个得怎么实现呢?查了下资料,发现有一个类叫 LinearGradient。这个类是干嘛用的呢?简单来说就是实现图形线性渐变的工具类,看了下构造函数,需要传以下参数
在这里插入图片描述
具体用法自行百度,直接贴代码

 LinearGradient linearGradient = new LinearGradient(mWidth/2,mHeight - dip2px(getContext(),150),mWidth/2,mHeight  + dip2px(getContext(),150),
                Color.parseColor("#F9F9F9"),Color.parseColor("#E9E9E9"), Shader.TileMode.MIRROR);
        mPaint.setShader(linearGradient);

在这里插入图片描述
恩,看起来差不多了(下面的颜色可能有点浅)。
接下来我们画内圆圈的部分,红色框 的内容
在这里插入图片描述

它也有一个渐变的过程,但是它的渐变的由外向里渐变的过程,怎么实现呢? 既然有线性渐变,肯定有其他类型的渐变类,看了下源码LinearGradient 继承于 Shader基类,看下Shader的实现类,
在这里插入图片描述
发现有个RadialGradient的类,可以用于环形渐变渲染。分析下,我们这个渐变颜色是由灰到白,构造函数为

RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)

重点关注两个参数colors和stops

  • int[] colors:表示所需要的渐变颜色数组
  • float[] stops:表示每个渐变颜色所在的位置百分点,取值0-1,数量必须与colors数组保持一致,不然直接crash,一般第一个数值取0,最后一个数值取1;如果第一个数值和最后一个数值并没有取0和1,比如我们这里取一个位置数组:{0.2,0.5,0.8},起始点是0.2百分比位置,结束点是0.8百分比位置,而0-0.2百分比位置和0.8-1.0百分比的位置都是没有指定颜色的。而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!有时效果我们是不可控的。所以为了方便起见,建议大家stop数组的起始和终止数值设为0和1.
//画内圈的圆弧
        RadialGradient radialGradient = new RadialGradient(mWidth/2,mHeight/2,radius - dip2px(getContext(),10),new int[]{Color.WHITE,Color.parseColor("#FFEBEBEB")}, new float[]{0.85f,1f},Shader.TileMode.CLAMP);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setShader(radialGradient);
        canvas.drawCircle(mWidth/2,mHeight/2,radius - dip2px(getContext(),10),mPaint);

由于不是平均区域渐变,所以将第二段position设在0.85f,运行后
在这里插入图片描述
接下来,画灰色指针,先画一根指针,然后再通过画笔旋转将所有指针画出,贴代码

        canvas.save();
        canvas.rotate(index * 3,mWidth/2,mHeight/2);
        for(int i=1; i<=120; i++){
            canvas.rotate(3,mWidth/2,mHeight/2);
            mLinePaint.setColor(Color.parseColor("#DBDBDB"));
            mLinePaint.setAlpha(255);
            canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mLinePaint);
        }
        canvas.restore()

在这里插入图片描述
然后再画蓝色指针,顺便逐渐改变画笔的透明度。效果如下

 //画蓝色线条
 if(i <= 30){
     mColorPaint.setAlpha((int) (i * 3 / 100f * 255) + 15);
     canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mColorPaint);
 }

在这里插入图片描述
是不是感觉有点像了,接下来,我们怎么让它旋转起来呢?
这里设置了一个Index进度值,我们每次重绘都让它的值改变,一担大于某个值,就让缩小,一旦小于某个值,就让它增大,每次根据该值旋转特定的角度, 如此循环,实现动画。

     if(index < 120){
            index ++;
        }else if(index == 120){
            index = 0;
        }
        canvas.save();
        canvas.rotate(index * 3,mWidth/2,mHeight/2);
        for(int i=1; i<=120; i++){
            canvas.rotate(3,mWidth/2,mHeight/2);
            mLinePaint.setColor(Color.parseColor("#DBDBDB"));
            mLinePaint.setAlpha(255);
            canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mLinePaint);
                if(i <= 30){
                    mColorPaint.setAlpha((int) (i * 3 / 100f * 255) + 15);
                    canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mColorPaint);
                }

        }
        canvas.restore();
        postInvalidate();

这样就实现了圆圈转动了~
接下来我们加上进度值,进度,用了一个percent变量、这个比较简单,就直接贴代码

      private int percent = 0;
      public void setPercent(int per){
        this.percent = per;
      }
      .....

        mTxtPaint.setColor(Color.BLACK);
        mTxtPaint.setTextSize(dip2px(getContext(),70));
        float textWidth = mTxtPaint.measureText(String.valueOf(percent));
        canvas.drawText(String.valueOf(percent),mWidth/2 - textWidth/2,mHeight/2 + 50 ,mTxtPaint);

        mTxtPaint.setTextSize(dip2px(getContext(),20));
        mTxtPaint.setColor(Color.GRAY);
        float Width = mTxtPaint.measureText(String.valueOf("%"));
        canvas.drawText("%",mWidth/2 + textWidth/2 - Width/2 + 25,mHeight/2 + 50 ,mTxtPaint);

为了能够让Loading停止,并且绘制对应的进度刻度,可以设置一个boolean变量isStop,在isStop为false的时候
绘制出对应的进度刻度并且停止重绘,onDraw完整的代码为

 @Override
    protected void onDraw(Canvas canvas) {

        LinearGradient linearGradient = new LinearGradient(mWidth/2,mHeight - dip2px(getContext(),150),mWidth/2,mHeight  + dip2px(getContext(),150),
                Color.parseColor("#F9F9F9"),Color.parseColor("#E9E9E9"), Shader.TileMode.MIRROR);
        mPaint.setShader(linearGradient);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor("#E1E1E1"));
        mPaint.setStrokeWidth(3);
        //画最外面的圆圈
        canvas.drawCircle(mWidth/2,mHeight/2,radius,mPaint);
        canvas.save();

        //画内圈的圆弧
        RadialGradient radialGradient = new RadialGradient(mWidth/2,mHeight/2,radius - dip2px(getContext(),10),new int[]{Color.WHITE,Color.parseColor("#FFEBEBEB")}, new float[]{0.85f,1f},Shader.TileMode.CLAMP);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setShader(radialGradient);
        canvas.drawCircle(mWidth/2,mHeight/2,radius - dip2px(getContext(),10),mPaint);

        if(index < 120){
            index ++;
        }else if(index == 120){
            index = 0;
        }
        canvas.save();
        canvas.rotate(index * 3,mWidth/2,mHeight/2);
        for(int i=1; i<=120; i++){
            canvas.rotate(3,mWidth/2,mHeight/2);
            mLinePaint.setColor(Color.parseColor("#DBDBDB"));
            mLinePaint.setAlpha(255);
            canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mLinePaint);

            if(!isStop){
                //画蓝色线条
                if(i <= 30){
                    mColorPaint.setAlpha((int) (i * 3 / 100f * 255) + 15);
                    canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mColorPaint);
                }
            }

        }
        canvas.restore();

        mTxtPaint.setColor(Color.BLACK);
        mTxtPaint.setTextSize(dip2px(getContext(),70));
        float textWidth = mTxtPaint.measureText(String.valueOf(percent));
        canvas.drawText(String.valueOf(percent),mWidth/2 - textWidth/2,mHeight/2 + 50 ,mTxtPaint);

        mTxtPaint.setTextSize(dip2px(getContext(),20));
        mTxtPaint.setColor(Color.GRAY);
        float Width = mTxtPaint.measureText(String.valueOf("%"));
        canvas.drawText("%",mWidth/2 + textWidth/2 - Width/2 + 25,mHeight/2 + 50 ,mTxtPaint);

        if(!isStop){
           postInvalidate();
        }else{
            //画进度实线
            int index = (int) (percent / 100f * 120);
            for(int j = 0; j < index; j++){
                mColorPaint.setAlpha(255);
                canvas.rotate(3,mWidth/2,mHeight/2);
                canvas.drawLine(mWidth/2,mHeight/2 - dip2px(getContext(),120),mWidth/2,mHeight/2 - dip2px(getContext(),105),mColorPaint);
            }
        }
    }

好了,这样就基本完成了。 基本不难,主要是一些细节的实现,比如渐变样式,还有画笔的旋转重绘使得刻度看起来处于旋转状态。

猜你喜欢

转载自blog.csdn.net/zz6880817/article/details/82811877