Android動畫篇(四):最終效果篇CircleProgressSuperBar
前言
今天終於有時間把最後的成果分享給大家了,為了提高一下部落格的逼格,我也找了一個專門做原型、導圖的線上網站:processon(www.processon.com),這個工具真的很棒,也很方便,這裡給他點個贊。
CircleProgressSuperBar為了完成最終的效果,我也是踩了一些坑,今天把我總結的最清晰的思路分享給大家,首先我們回顧一下我們的效果圖:
正文
首先我們來分析一下,CircleProgressSuperBar總共分為幾種狀態:
這四種狀態,我們不關心是怎麼切換的,我們只關心動畫是怎麼過渡的。
首先我們知道準備一些類:
1、CircleProgressSuperBar,這個是主類,也是最終要完成的view。
2、四個狀態的Drawable,我們分別命名為:NormalDrawable、LoadingDrawable、CompleteDrawable和ErrorDrawable。
最終為了方便擴充套件和解耦,我最終實現的架構圖是這樣的:
我的主要目的:
1、在View和Drawable之間建立工廠類,即能降低類之間的耦合,也可以方便擴充套件更多狀態的Drawable。
2、各種狀態Drawble的基類BaseStatusDrawable,封裝公共的屬性和方法,讓View直接使用BaseStatusDrawable型別,而不去具體關心具體的Drawable的實現。
3、BaseStatusDrawable內部帶有樣式的資訊,防止和內部的畫筆有關的顏色弄混。
那我們就從最基礎的部分,首先新建CircleProgressSuperInfo:
public class CircleProgressSuperInfo {
/**
* 寬, 在設定動畫的時候需要知道寬
*/
private int mWidth;
/**
* 高,在設定動畫的時候需要知道高
*/
private int mHeight;
/**
* 圓角
*/
private int mRadius;
/**
* 背景顏色
*/
private int mBgColor;
/**
* 邊框顏色
*/
private int mBorderColor;
/**
* 邊框的寬度
*/
private int mBorderWidth;
/**
* 最大的間距
* */
private float mPadding;
public CircleProgressSuperInfo(int bgColor, int borderColor, int borderWidth) {
this.mBgColor = bgColor;
this.mBorderColor = borderColor;
this.mBorderWidth = borderWidth;
}
...
// 此處省略setter和getter方法
}
然後就是需要BaseStatusDrawable,我們直接把之前寫好的形狀變化的ChangeShapeAndColorButton進行改造,變成我們需要的BaseStatusDrawable:
/**
* Created by li.zhipeng on 2017/7/12.
* <p>
* 所有的狀態圖片需要實現此介面
*/
public abstract class BaseStatusDrawable extends Drawable {
protected CircleProgressSuperInfo mInfo;
/**
* 寬, 在設定動畫的時候需要知道寬
*/
protected int mWidth;
/**
* 高,在設定動畫的時候需要知道高
*/
protected int mHeight;
/**
* 圓角
*/
protected float mRadius;
/**
* 文字
*/
protected String mText;
/**
* 文字顏色
*/
protected int mTextColor = Color.parseColor("#ffffff");
/**
* 文字大小
*/
protected int mTextSize;
/**
* 畫筆
*/
protected Paint mPaint;
/**
* 形狀
*/
protected RectF mRectF;
/**
* 背景顏色
*/
protected int mBgColor;
/**
* 邊框顏色
*/
protected int mBorderColor;
/**
* 邊框的寬度
*/
protected int mBorderWidth;
/**
* 偏移值,也就是大小要發生的變化值
*/
protected float mPadding;
/**
* 最小大小
*/
protected float mMinSize = -1;
/**
* 正在動畫在中
*/
protected boolean isAnim;
public BaseStatusDrawable(CircleProgressSuperInfo info) {
this.mInfo = info;
// 初始化畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setDither(true);
mRectF = new RectF();
}
// 此處省略各種setter和getter方法
...
public CircleProgressSuperInfo getInfo(){return this.mInfo;};
/**
* 回到後臺,呼叫此方法
*/
public abstract void release();
/**
* 繪製
*/
@Override
public void draw(@NonNull Canvas canvas) {
// 這是繪製過渡動畫
if (isAnim()) {
drawTransition(canvas);
}
// 繪製正常狀態,例如loading就要使用繪製旋轉的圓圈
else {
drawSelf(canvas);
}
}
/**
* 繪製過度動畫
*/
protected void drawTransition(Canvas canvas) {
// 先畫出背景,背景是居中的
// 判斷寬高
int width = getWidth();
int height = getHeight();
// 計算左右的間距值,並且判斷不能小於minSize
float paddingLR = width - mPadding * 2 < mMinSize ? (width - mMinSize) / 2 : mPadding;
float paddingTB = height - mPadding * 2 < mMinSize ? (height - mMinSize) / 2 : mPadding;
// 繪製描邊
mRectF.set(paddingLR + mBorderWidth / 2, paddingTB + mBorderWidth / 2, getWidth() - paddingLR - mBorderWidth / 2,
getHeight() - paddingTB - mBorderWidth / 2);
// 開始畫後面的背景
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mBgColor);
canvas.drawRoundRect(mRectF, mRadius, mRadius, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
canvas.drawRoundRect(mRectF, mRadius, mRadius, mPaint);
// 居中繪製文字
if (!TextUtils.isEmpty(mText)) {
float textDescent = mPaint.getFontMetrics().descent;
float textAscent = mPaint.getFontMetrics().ascent;
float delta = Math.abs(textAscent) - textDescent;
mPaint.setColor(mTextColor);
mPaint.setTextSize(mTextSize);
float textWidth = mPaint.measureText(mText);
canvas.drawText(mText, (width - textWidth) / 2, height / 2 + delta / 2, mPaint);
}
}
/**
* 繪製正常狀態
*/
public abstract void drawSelf(Canvas canvas);
@Override
public void setAlpha(@IntRange(from = 0, to = 255) int i) {
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
這裡要強調幾點:
1、繼承Drawable的原因:主要是為了重繪,例如之後的載入狀態,是需要不斷重繪的才會有轉圈的動畫,但是已經和View分離了,就無法藉助View.invaliate(),所以這裡繼承了Drawable。
2、drawSelf()是繪製非過渡動畫的狀態,這裡主要是給LoadingDrawable用的。
3、getOpacity()方法的作用:返回圖片的質量型別。
為了理解getOpacity的作用,我們就先看一下他的原始碼和註釋:
// 擷取的註釋,getOpacity值能返回一下幾個值
PixelFormat.UNKNOWN
PixelFormat.TRANSLUCENT
PixelFormat.TRANSPARENT
PixelFormat.OPAQUE
// 系統自動適配
public static final int UNKNOWN = 0;
// 簡單的說就是支援半透明
public static final int TRANSLUCENT = -3;
// 支援全透明
public static final int TRANSPARENT = -2;
// 不支援透明
public static final int OPAQUE = -1;
屬性的命名規則就是見字如見人,字面意思就是這樣。其他兩個方法,這裡沒有用到就不說明了,有興趣的可以自己去研究研究。
這個時候去就可以建立四個狀態的Drawable了,普通狀態、完成狀態還有錯誤狀態都是一樣的,所以就只貼一個類的程式碼了:
/**
* Created by li.zhipeng on 2017/7/12.
* <p>
* 正常狀態下的圖片
*/
public class NormalDrawable extends BaseStatusDrawable {
public NormalDrawable(CircleProgressSuperInfo info) {
super(info);
// 普通狀態的顏色要設定,其他的不用設定
setBgColor(info.getBgColor());
setBorderColor(info.getBorderColor());
}
@Override
public void drawSelf(Canvas canvas) {
drawTransition(canvas);
}
@Override
public void release() {
}
}
重點是LoadingDrawable,其實也很簡單,因為繼承的關係,現在只需要關心繪製載入狀態就足夠了,這個時候把我們第一篇CircleProgressBar進行改造:
/**
* Created by li.zhipeng on 2017/7/12.
* <p>
* 載入狀態或是進度條的drawable
*/
public class LoadingDrawable extends BaseStatusDrawable {
/**
* 圓周的角度
*/
private static final Float CIRCULAR = 360f;
/**
* 進度
*/
private float mProgress = 50;
/**
* 最大進度
*/
private int mMaxProgress = 100;
/**
* 邊框顏色,也就是進度的顏色
*/
private int mProgressColor = Color.parseColor("#ff00ff");
/**
* 繪製的不全進度的顏色
*/
private int mDrawBorderColor;
/**
* 要繪製的進度條的顏色
*/
private int mDrawProgressColor;
/**
* 是否開啟過度模式,也就是我們平時看到的類似追趕的效果
*/
private boolean mIsIntermediateMode = true;
/**
* 最小弧度,進度條過度模式最小的弧度
*/
private int mMinProgress = 5;
/**
* 過度動畫的時間
*/
private static final int DURATION = 1000;
/**
* 過度動畫
*/
private ValueAnimator valueAnimator;
/**
* 開始角度,在過度動畫中使用
*/
private float mStartAngle = -90f;
public LoadingDrawable(CircleProgressSuperInfo info) {
super(info);
}
/**
* 設定進度
*/
public void setProgress(float progress) {
this.mProgress = progress;
}
/**
* 獲取進度條的顏色
*/
public int getProgressColor() {
return this.mProgressColor;
}
/**
* 設定進度條的顏色
*/
public void setProgressColor(int color) {
this.mProgressColor = color;
}
/**
* 設定進度條的顏色
*/
public void setDrawProgressColor(int color) {
this.mDrawProgressColor = color;
}
public int getDrawProgressColor() {
return mDrawProgressColor;
}
public int getDrawBorderColor() {
return mDrawBorderColor;
}
public void setDrawBorderColor(int mDrawBorderColor) {
this.mDrawBorderColor = mDrawBorderColor;
}
/**
* 是否是過度模式
*/
public boolean isIntermediateMode() {
return mIsIntermediateMode;
}
/**
* 設定繪製區域
*/
@Override
public void setRadius(float radius) {
super.setRadius(radius);
// 計算要繪製的區域
mRectF.set(mWidth / 2 - mRadius + mBorderWidth / 2, mHeight / 2 - mRadius + mBorderWidth / 2,
mWidth / 2 + mRadius - mBorderWidth / 2, mHeight / 2 + mRadius - mBorderWidth / 2);
}
/**
* 設定loading模式
*/
public void setIntermediateMode(boolean intermediateMode) {
if (mIsIntermediateMode != intermediateMode) {
this.mIsIntermediateMode = intermediateMode;
// 取消動畫
if (!mIsIntermediateMode) {
valueAnimator.cancel();
} else {
//這裡要開啟動畫
startIntermediateAnim();
}
}
}
@Override
public void drawSelf(Canvas canvas) {
// 是否要顯示loading狀態
if (mIsIntermediateMode) {
startIntermediateAnim();
drawIntermediateProgress(canvas);
}
// 繪製進度條
else {
drawProgress(canvas);
}
}
/**
* 繪製過度進度條
*/
private void drawIntermediateProgress(Canvas canvas) {
// 首先畫出背景圓
mPaint.setColor(mBgColor);
mPaint.setStyle(Paint.Style.FILL);
// 這裡減去了邊框的寬度
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius - mBorderWidth, mPaint);
// 畫出進度條
mPaint.setColor(mDrawProgressColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mBorderWidth);
// 計算圓弧劃過的角度
float angle = CIRCULAR / mMaxProgress * mProgress;
// 這裡要畫圓弧
canvas.drawArc(mRectF, mStartAngle, angle, false, mPaint);
// 畫出另一部分的進度條
mPaint.setColor(mDrawBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
// 這裡要畫圓弧
canvas.drawArc(mRectF, mStartAngle + angle, CIRCULAR - angle, false, mPaint);
}
/**
* 繪製進度條
*/
private void drawProgress(Canvas canvas) {
// 開始畫進度條
// 首先畫出背景圓
mPaint.setColor(mBgColor);
mPaint.setStyle(Paint.Style.FILL);
// 這裡減去了邊框的寬度
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius - mBorderWidth, mPaint);
// 畫出進度條
mPaint.setColor(mDrawProgressColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mBorderWidth);
// 計算圓弧劃過的角度
float angle = CIRCULAR / mMaxProgress * mProgress;
// 這裡要畫圓弧
canvas.drawArc(mRectF, -90, angle, false, mPaint);
// 畫出另一部分的進度條
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
// 這裡要畫圓弧
canvas.drawArc(mRectF, -90 + angle, CIRCULAR - angle, false, mPaint);
}
/**
* 開始過度動畫
*/
private synchronized void startIntermediateAnim() {
if (valueAnimator != null && valueAnimator.isStarted()) {
return;
}
if (valueAnimator == null) {
valueAnimator = new ValueAnimator().ofFloat(mMinProgress, mMaxProgress - mMinProgress);
valueAnimator.setDuration(DURATION);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) valueAnimator.getAnimatedValue();
setProgress(value);
mStartAngle += 2;
invalidateSelf();
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
// 充值旋轉的角度
mStartAngle = -90;
}
@Override
public void onAnimationRepeat(Animator animator) {
// 互換顏色和位置
mStartAngle = mStartAngle - CIRCULAR / mMaxProgress * mMinProgress;
int color = getDrawProgressColor();
setDrawProgressColor(getDrawBorderColor());
setDrawBorderColor(color);
}
});
}
// 開始動畫的時候,要重新設定顏色,否則顏色可能會錯亂,因為在動畫的過程已經互換
setDrawProgressColor(mProgressColor);
setDrawBorderColor(mBorderColor);
valueAnimator.setRepeatCount(-1);
valueAnimator.start();
}
/**
* 停止動畫
*/
private void stopAnim() {
if (valueAnimator != null && valueAnimator.isRunning()) {
valueAnimator.cancel();
}
}
@Override
public void release() {
stopAnim();
}
}
幾乎是沒有什麼變化,增加了開始動畫和結束動畫方法,這樣其他的狀態時,可以節省系統資源。
然後是工廠類:
/**
* Created by li.zhipeng on 2017/7/13.
* <p>
* 生產不同狀態的Drawable的生產類
*/
public class StatusDrawableFactory {
private static StatusDrawableFactory mInstance;
public synchronized static StatusDrawableFactory getInstance() {
if (mInstance == null) {
mInstance = new StatusDrawableFactory();
}
return mInstance;
}
/**
* 返回指定狀態的drawable
* */
public BaseStatusDrawable getDrawable(int status, CircleProgressSuperInfo info) {
BaseStatusDrawable drawable = null;
switch (status) {
case Status.NORMAL:
drawable = new NormalDrawable(info);
break;
case Status.LOADING:
drawable = new LoadingDrawable(info);
break;
case Status.COMPLETE:
drawable = new CompleteDrawable(info);
break;
case Status.ERROR:
drawable = new ErrorDrawable(info);
break;
}
return drawable;
}
}
非常簡單的單例模式,返回指定的BaseStatusDrawable型別。
最後就是CircleProgressSuperBar:
/**
* Created by li.zhipeng on 2017/7/12.
* <p>
* 具有多狀態的CircleProgressBar,整合前兩個控制元件的效果
*/
public class CircleProgressSuperBar extends View {
/**
* 儲存四張狀態的Drawable
*/
private BaseStatusDrawable[] drawables = new BaseStatusDrawable[4];
/**
* 測試就只要一個xml的構造方法就足夠了
*/
public CircleProgressSuperBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 初始化資訊類
drawables[Status.NORMAL] = StatusDrawableFactory.getInstance().getDrawable(Status.NORMAL,
new CircleProgressSuperInfo(Color.parseColor("#3399ff"), Color.parseColor("#3399ff"), 10));
drawables[Status.LOADING] = StatusDrawableFactory.getInstance().getDrawable(Status.LOADING,
new CircleProgressSuperInfo(Color.parseColor("#ff0000"), Color.parseColor("#000000"), 10));
drawables[Status.COMPLETE] = StatusDrawableFactory.getInstance().getDrawable(Status.COMPLETE,
new CircleProgressSuperInfo(Color.parseColor("#ffcc00"), Color.parseColor("#ffcc00"), 10));
drawables[Status.ERROR] = StatusDrawableFactory.getInstance().getDrawable(Status.ERROR,
new CircleProgressSuperInfo(Color.parseColor("#ff3300"), Color.parseColor("#ff3300"), 10));
drawables[Status.NORMAL].setText("Normal");
drawables[Status.ERROR].setText("Error");
drawables[Status.COMPLETE].setText("Complete");
drawables[Status.NORMAL].setTextSize(42);
drawables[Status.ERROR].setTextSize(42);
drawables[Status.COMPLETE].setTextSize(42);
drawables[Status.LOADING].setBorderWidth(20);
// 設定重繪回撥
drawables[Status.LOADING].setCallback(this);
}
/**
* 動畫時長
*/
private int mDuration = 500;
/**
* 目前的狀態t
*/
private int mCurrentStatus = Status.NORMAL;
/**
* 是否正在動畫中
*/
private boolean isAnim;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 這裡設定一些初始值
int width = getMeasuredWidth();
int height = getMeasuredHeight();
drawables[Status.NORMAL].setWidth(width);
drawables[Status.NORMAL].setHeight(height);
drawables[Status.LOADING].setWidth(width);
drawables[Status.LOADING].setHeight(height);
drawables[Status.ERROR].setWidth(width);
drawables[Status.ERROR].setHeight(height);
drawables[Status.COMPLETE].setWidth(width);
drawables[Status.COMPLETE].setHeight(height);
// 設定的Radius
int radius = width > height ? height / 2 : width / 2;
drawables[Status.NORMAL].setMinSize(radius * 2);
drawables[Status.LOADING].setMinSize(radius * 2);
drawables[Status.ERROR].setMinSize(radius * 2);
drawables[Status.COMPLETE].setMinSize(radius * 2);
drawables[Status.LOADING].getInfo().setRadius(radius);
drawables[Status.LOADING].getInfo().setPadding(width > height ? (width - radius * 2) / 2 : (height - radius * 2) / 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 畫出不同狀態的內容
drawables[mCurrentStatus].draw(canvas);
}
/**
* 設定狀態
*/
public void setStatus(int status) {
if (mCurrentStatus != status && !isAnim) {
// 這裡設定動畫效果
changeStatus(mCurrentStatus, status);
// 釋放之前的動畫
this.drawables[mCurrentStatus].release();
this.mCurrentStatus = status;
this.drawables[mCurrentStatus].setIsAnim(true);
}
}
/**
* 狀態改變的動畫
*/
private void changeStatus(int fromStatus, int toStatus) {
isAnim = true;
// 取出相關的動畫資訊
CircleProgressSuperInfo fromStatusInfo = drawables[fromStatus].getInfo();
CircleProgressSuperInfo toStatusInfo = drawables[toStatus].getInfo();
// 開始動畫
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(mDuration);
animatorSet.playTogether(AnimUtil.getColorAnim(fromStatusInfo.getBgColor(), toStatusInfo.getBgColor(), mDuration, colorUpdateListener),
AnimUtil.getColorAnim(fromStatusInfo.getBorderColor(), toStatusInfo.getBorderColor(), mDuration, borderColorUpdateListener),
AnimUtil.getRadiusAnim(fromStatusInfo.getRadius(), toStatusInfo.getRadius(), mDuration, radiusUpdateListener),
AnimUtil.getShapeAnim(fromStatusInfo.getPadding(), toStatusInfo.getPadding(), mDuration, shapeUpdateListener)
);
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
isAnim = false;
drawables[mCurrentStatus].setIsAnim(false);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
animatorSet.start();
}
/**
* color動畫的回撥
*/
private ValueAnimator.AnimatorUpdateListener colorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
drawables[mCurrentStatus].setBgColor((Integer) valueAnimator.getAnimatedValue());
invalidate();
}
};
/**
* borderColor動畫的回撥
*/
private ValueAnimator.AnimatorUpdateListener borderColorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
drawables[mCurrentStatus].setBorderColor((Integer) valueAnimator.getAnimatedValue());
}
};
/**
* radius動畫的回撥
*/
private ValueAnimator.AnimatorUpdateListener radiusUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
drawables[mCurrentStatus].setRadius((float) valueAnimator.getAnimatedValue());
}
};
/**
* shape動畫的回撥
*/
private ValueAnimator.AnimatorUpdateListener shapeUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
drawables[mCurrentStatus].setPadding((Float) valueAnimator.getAnimatedValue());
}
};
/**
* Invalidates the specified Drawable.
*
* @param drawable the drawable to invalidate
*/
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
invalidate();
}
}
主要是找到各個狀態的BaseStatusDrawable,然後取出資訊,開始屬性動畫,但是有幾個小知識點,你還記得嗎?
1、在獲取指定狀態的圖片,直接使用Status.xxx作為陣列的索引,儲存和取出的速度都很快,是不是想起之前我們聊過的雜湊表了?
2、onMeasure方法裡,記得使用getMeasuredXXX,因為getWidth和getHeight都是0,千萬別忘了。
有些朋友發現了:怎麼突然冒出來一個invalidateDrawable()方法?我這裡直說了,大家想自己去踩坑的可以試試:
還記得之前說過的繼承Drawable是為了重繪嗎,如果是你只是呼叫了Drawable.invalidateSelf(),很遺憾的告訴你,是不可能重繪的,所以這裡要重寫這個方法,強制重繪。
直接看原始碼就知道原因了:
//Drawable的重繪方法,實際上是呼叫了callback,這樣就和View解耦了
public void invalidateSelf() {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
// view本身就實現了Callback
public class View implements Drawable.Callback{
// 請注意裡面的判斷
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
// 滿足了這個條件,才會重繪,所以要看看判斷條件是什麼
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
rebuildOutline();
}
}
@CallSuper
protected boolean verifyDrawable(@NonNull Drawable who) {
// 這裡就是判斷,view要判斷是否使用了這個Drawable,如果沒有使用,就不去重繪了,這個理論都是可以理解的。
return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
}
}
因為我們僅僅是canvas繪圖,並沒有設定背景或者是前景圖片之類的東西,自然就無法重繪,也就是重寫invalidateDrawable()方法的原因。
還有兩個類,沒有貼出來:Status(Drawable的狀態),AnimUtil(動畫工具類),因為感覺今天的內容已經很長了,所以就省略了把,大家可以在demo中去檢視。
總結
看的說的挺溜,其實在寫的時候還是出現了很多問題的,而且現在也還存在一些小問題,如果你發現部落格中的程式碼和demo中有一點點區別,那就是我後來又修改了,但是主要思想是不會變了,大家可以自己去設定自定義屬性,這樣我們的完成度就更完美了。