实现一个Material效果的ProgressBar

来源:互联网 发布:淘宝号出售批发 编辑:程序博客网 时间:2024/05/20 04:09

先来看看效果:
这里写图片描述

分析下难点:
1. 动画的实现;
2. 边界的控制;
3. 状态保存与恢复;
4. 两种状态的实现,loading状态(不停旋转)、progress状态。

分别来看下。
1. 动画如何实现:
将动画进行拆解,可以发现它其实是一个弧不断变长变短的一个过程+弧本身在绕圆形转动两部分组成。
所以可以分开来处理,弧度变长变短可以通过canvas.drawArc的参数startAngle/SweeepAngle控制,只要改变这两个值即可实现效果。怎么改变?有几种方案,1是通过hander+thread;2是通过View.post();3是通过PropertyAnimation.
弧本身绕圆心运动可以通过Canvas.rotate实现。

private static final float delta = 6f;    private float temp = 0;    class AnimRunnable implements Runnable{        @Override        public void run() {            if (mStartAngle == temp) {                mSweepAngle += delta;            }            if (mSweepAngle >= 280 || mStartAngle > temp) {                mStartAngle += delta;                if(mSweepAngle > 20) {                    mSweepAngle -= delta;                }            }            if (mStartAngle > temp + 280) {                temp = mStartAngle;                mStartAngle = temp;                mSweepAngle = 20;            }            postInvalidate();            postDelayed(this,mSpinSpeed);        }    }

2.边界的控制:
需要在onMeasure中控制。在onSizeChanged方法中可以拿到最终的width、height,通过width/height就可以控制progressbar的边界了。
需要注意的是,边界需要是正方形的,所以得考虑宽高不相等的情况以及四个方向padding的大小。

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //计算自己需要的宽度和高度        int width = mCircleRadius*2;        int height = mCircleRadius*2;        //考虑父容器的测量规则        setMeasuredDimension(getResolvedSize(width, widthMeasureSpec), getResolvedSize(height, heightMeasureSpec));    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        int paddingLeft = getPaddingLeft();        int paddingTop = getPaddingTop();        int paddingRight = getPaddingRight();        int paddingBottom = getPaddingBottom();        //简化处理,以最大的padding作为padding        int padding = Math.max(Math.max(paddingLeft, paddingRight), Math.max(paddingTop, paddingBottom));        int diameter;        //保证bounds是一个正方形        if(w >= h){            diameter = h;            mBounds = new RectF(padding+mBarWidth+(w-h)/2,padding+mBarWidth,diameter-padding-mBarWidth+(w-h)/2,diameter-padding-mBarWidth);        }else if(w < h){            diameter = w;            mBounds = new RectF(padding+mBarWidth,padding+mBarWidth+(h-w)/2,diameter-padding-mBarWidth,diameter-padding-mBarWidth+(h-w)/2);        }    }

3.状态的保存与恢复:
progressbar的状态不能因为横竖屏切换等问题丢失,所以需要通过重写onSaveInstanceState/onRestoreInstanceState来保存/恢复状态.

@Override    protected void onRestoreInstanceState(Parcelable state) {        if(! (state instanceof SavedState)){            super.onRestoreInstanceState(state);            return;        }        //先恢复父类的状态        SavedState savedState = (SavedState) state;        super.onRestoreInstanceState(savedState.getSuperState());        //在恢复自己的状态        this.mCurMode = savedState.mCurMode == 0 ? Mode.INDETERMINATE : Mode.DETERMINATE;        this.mRimWidth = savedState.mRimWidth;        this.mRimColor = savedState.mRimColor;        this.mBarColor = savedState.mBarColor;        this.mBarWidth = savedState.mBarWidth;        this.showRim = savedState.showRim;        this.isAnimStart = savedState.isAnimStart;        this.mProgress = savedState.mProgress;    }    @Override    protected Parcelable onSaveInstanceState() {        //相当于是做了一层包装        //先保存父类的状态,然后包装,再保存自己的状态        Parcelable parcelable = super.onSaveInstanceState();        SavedState savedState = new SavedState(parcelable);        savedState.mCurMode = (this.mCurMode == Mode.INDETERMINATE) ? 0 : 1;        savedState.mRimWidth = this.mRimWidth;        savedState.mRimColor = this.mRimColor;        savedState.mBarColor = this.mBarColor;        savedState.mBarWidth = this.mBarWidth;        savedState.showRim = this.showRim;        savedState.isAnimStart = this.isAnimStart;        savedState.mProgress = this.mProgress;        return savedState;    }

4.两种状态的实现:
自然是通过一个变量记录当前模式,在onDraw中通过判断模式进行不同的绘制操作。

地址:​https://github.com/Rowandjj/MaterialProgressBar/

3 0