Android自定義View之自定義載入進度條(二)
本文首發於公眾號“AntDream”,歡迎微信搜尋“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點
自定義載入進度條
上次我們已經把實線和虛線都繪製好了,這次我們就主要來解決更新的問題:
- 怎麼隨著時間的推移逐漸地繪製進度條
- 怎麼在繪製的過程中加速進度條的繪製
首先我們來解決第一個問題,也就是隨著時間更新我們的進度。
其實原理也很簡單,上次我們已經提到用Canvas的drawArc方法來繪製進度條,只需要改變繪製的角度就可以。所以我們可以利用時間差值器,在一段時間內讓角度從0度變化到360度,也就是完整的一圈了。
更新進度
- 首先我們要設定一個範圍,這個範圍初始值為0,表示初始狀態;最大值為100,表示全部繪製完成
float mCurrentValue = 0.0F; float mMaxValue = 100.0F;
- 有了範圍以後,我們就可以計算需要繪製的角度了
float degrees = 360.0F / this.mMaxValue * this.mCurrentValue;
- 然後我們就可以繪製我們的進度條啦
canvas.drawArc(this.mCircleBounds, (float) this.mStartAngle, degrees, false, this.mBarPaint);
這裡的mStartAngle表示起始角度,因為我們的進度條一般都是從最上面開始,所以這裡是270度
- 這樣子的話我們就只需要改變我們的mCurrentValue值就行了。
//線性時間差值器 TimeInterpolator mInterpolator = new LinearInterpolator();
隨著時間計算mCurrentValue的值
float t = (float)((double)(System.currentTimeMillis() - this.mAnimationStartTime) / circleView.mAnimationDuration); t = t > 1.0F ? 1.0F : t; float interpolatedRatio = this.mInterpolator.getInterpolation(t); //這裡的mValueTo表示我們的最大的範圍,也就是100,而mValueFrom用於暫存當前的進度 circleView.mCurrentValue = circleView.mValueFrom + (circleView.mValueTo - circleView.mValueFrom) * interpolatedRatio;
通過以上的幾步我們基本就解決了隨著時間重新整理我們的進度條的問題。
需要注意的是更新我們的進度條最好要通過Handler來更新,因為外面呼叫者並不一定是在UI執行緒發起更新我們UI的呼叫的。
public class AnimationHandler extends Handler{ private long mAnimationStartTime; private TimeInterpolator mInterpolator = new LinearInterpolator(); //這裡要用弱引用,防止記憶體洩漏 private final WeakReference<DashCircleProgressView> mCircleViewWeakReference; public AnimationHandler(DashCircleProgressView dashCircleProgressView) { super(dashCircleProgressView.getContext().getMainLooper()); this.mCircleViewWeakReference = new WeakReference(dashCircleProgressView); } public void setValueInterpolator(TimeInterpolator interpolator) { this.mInterpolator = interpolator; } @Override public void handleMessage(Message msg) { ... case ANIMATING: switch (msgType) { case SET_VALUE: this.setValue(msg, circleProgressView); break; case SET_VALUE_ANIMATED: break; case TICK: if (this.calcNextAnimationValue(circleProgressView)) { circleProgressView.mAnimationState = AnimationState.IDLE; circleProgressView.mCurrentValue = circleProgressView.mValueTo; } this.sendEmptyMessageDelayed(AnimationMsg.TICK.ordinal(), (long)circleProgressView.mFrameDelayMillis - (SystemClock.uptimeMillis() - this.mFrameStartTime)); //重新整理 circleProgressView.invalidate(); break; default: break; } break; } //判斷是否還能更新,並根據差值器更新mCurrentValue值 private boolean calcNextAnimationValue(DashCircleProgressView circleView) { float t = (float)((double)(System.currentTimeMillis() - this.mAnimationStartTime) / circleView.mAnimationDuration); t = t > 1.0F ? 1.0F : t; float interpolatedRatio = this.mInterpolator.getInterpolation(t); circleView.mCurrentValue = circleView.mValueFrom + (circleView.mValueTo - circleView.mValueFrom) * interpolatedRatio; return t >= 1.0F; } ... }
需要注意的是,我們對於UI的引用採用了弱引用,為的是防止記憶體洩漏。具體的原因可以檢視文章: Android記憶體優化之記憶體洩漏
最後我們還需要對外提供初始設定和啟動更新進度的介面
//valueTo表示更新的目標值;animationDuration表示更新的時間 public void setValueAnimated(float _valueTo, long _animationDuration) { this.setValueAnimated(this.mCurrentValue, _valueTo, _animationDuration); } //最終內部是通過Handler來更新我們的UI public void setValueAnimated(float _valueFrom, float _valueTo, long _animationDuration) { this.mAnimationDuration = (double) _animationDuration; Message msg = new Message(); msg.what = AnimationMsg.SET_VALUE_ANIMATED.ordinal(); msg.obj = new float[]{_valueFrom, _valueTo}; this.mAnimationHandler.sendMessage(msg); } //設定進度 public void setValue(float _value) { Message msg = new Message(); msg.what = AnimationMsg.SET_VALUE.ordinal(); msg.obj = new float[]{_value, _value}; this.mAnimationHandler.sendMessage(msg); }
如何加快我們的進度條
其實上面提供的介面已經給出了答案,我們可以通過縮短更新的時間來達到加快動畫的效果。比如原來是設定10秒鐘載入完進度,進度載入到10%的時候,發現需要加快進度,這個時候我們就可以設定進度載入的時間為2秒鐘,這樣看起來就加快了。
//通過這2個介面的設定,我們就將剩下的進度在2秒鐘內載入完畢 circleView.setValue(circleView.getCurrentValue()); circleView.setValueAnimated(circleView.getCurrentValue(), 100, 2000);
總結
通過2篇文章,簡單梳理了自定義一個載入進度條的過程。其實自定義View最重要的是理清思路,然後一步步去實現,最後才是慢慢地優化和嘗試複雜的自定義View。
這裡面涉及到的知識點主要是:
- View的繪製原理
- 訊息機制
- 記憶體洩漏
- 時間差值器的使用
- 基本的Paint和Canvas的使用
希望能幫到大家
歡迎關注我的微信公眾號,和我一起每天進步一點點!

AntDream