本文按自己實現(xiàn)的思路整理成博客供參考和日后復(fù)習(xí),直接上效果

- 1、控件分析
- 2、巧用策略模式
- 3、優(yōu)化
- 4、工程源碼
1.控件分析
1.1狀態(tài)分析
初始狀態(tài):它的背景是一個圓
清理狀態(tài):背景圓先縮小成一個小圓,并帶動幾個小圓繞圓環(huán)運行,并且這些小圓之間的距離先拉開后又縮進重貼,依次反復(fù)。
清理完成:小圓拉開,向圓環(huán)中心聚合,背景圓有小擴大到初始狀態(tài)(顏色根據(jù)清理程度顯示不同顏色)
增強效果:以背景圓圓心為中心,擴散出很多大小、半徑、顏色、運行距離不同的小圓,并最終消失。(隨機的,即每次清理完,擴散小圓個數(shù),大小、透明度等等都是隨機的)
1.2定義屬性
分析哪些屬性可變,定義成屬性,支持布局文件中配置。個人習(xí)慣于先實現(xiàn)效果,最后根據(jù)需求再從代碼中分離出自定義屬性。
1.3 onMeasure測量
這個步驟不需要也行,但為了控件效果,寬高太小肯定不好看,所以測量過程根據(jù)給定的寬高和控制要求最小的寬高進行比較,取大值。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int minHW = DensityUtils.dp2px(mMinHeightAndWidth);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
height = Math.min(width, height);
height = height > minHW ? height : minHW;
mRadius = height / 4;
setMeasuredDimension(width, height);
}
minHW 是控件要求的最小寬高。之后所有的狀態(tài)都在控件寬高內(nèi)繪制。
1.4 onDraw繪制
分析
根據(jù)效果圖中,整個過程中出現(xiàn)的幾何圖形都是圓形,所以整個過程中也只涉及到一個API:canvas.drawCircle(cx,cy,radius,paint);
drawCircle這個方法的四個參數(shù):圓心的坐標(biāo)x和y,半徑radius,畫筆paint決定顏色,透明度等。
圓要運動或顯示在不同位置,只要改變x,y
圓大小變化,由半徑radius決定;
圓的顏色透明度等由paint設(shè)置。
所以不論何種狀態(tài),圓的動畫的運行原理就改變x,y,或者半徑radius或顏色。畫出對應(yīng)的圓,原理比較簡單。既然每個階段都是畫圓,并根據(jù)一定的算法不斷地改變圓坐標(biāo)或半徑,根據(jù)新的參數(shù)繪制圓,讓其“動起來”,所以動畫的執(zhí)行算法和每個狀態(tài)過度之間的動畫才是我們需要深入考慮的。
按上述的分析,定義方法 drawBackground(Canvas canvas) 用于畫背景圓
定義方法drawBoll(Canvas canvas)用于畫繞圓環(huán)運動的圓等等,讓后在onDraw(Canvas canvas)中根據(jù)條件調(diào)用不同的畫方法,每個draw方法對應(yīng)一個valueAnimator,執(zhí)行具體的運動算法(詳細看代碼),不同的狀態(tài)下執(zhí)行不同的valueAnimator。哇,這樣看來顯得過于復(fù)雜。

每一種狀態(tài),都是一種畫方法(draw)對應(yīng)一種運動算法(valueAnimator)。所以可以抽取為一種“狀態(tài)”如下

public abstract class ClearViewAnim {
/**
* 執(zhí)行繪畫
*
* @param canvas 畫布
*/
public abstract void drawState(Canvas canvas);
/**
* 執(zhí)行動畫
*/
public abstract void startAnim();
/**
* 停止動畫
*/
public abstract void stopAnim();
}
讓各種狀態(tài)都繼承ClearViewAnim ,讓后各自實現(xiàn)自己的畫方法和運動動畫。

//定義初始狀態(tài)
class InitAnim extends ClearViewAnim{
public InitAnim(int status) {
currentState = status;
//初始化valueAnimator 定義具體的運動算法
}
@Override
public void drawState(Canvas canvas) {
//調(diào)用具體的繪制方法
drawBackground(canvas);
}
@Override
public void startAnim() {
valueAnimator.start();
}
@Override
public void stopAnim() {
valueAnimator.cancel();
}
}
//清理狀態(tài)
class RunningAinm extends ClearViewAnim {
//省略...
}
//清理完成狀態(tài)
class FinishAinm extends ClearViewAnim {
//省略...
}
//增強效果狀態(tài)
class ExpendAmin extends ClearViewAnim {
//省略...
}
這個時候onDraw方法就變成這樣,代碼非常簡潔。
//動畫狀態(tài)抽象類
private ClearViewAnim mViewAnim;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mViewAnim == null) {
//初始狀態(tài)InitAnim
mViewAnim = new InitAnim(STATE_INIT);
}
mViewAnim.drawState(canvas);
}
由于mViewAnim 是抽象類, mViewAnim.drawState(canvas)具體執(zhí)行什么狀態(tài)下的畫方法,由其子類決定的,所以根據(jù)不同的時機將mViewAnim替換成InitAnim、RunningAinm 、FinishAinm 、ExpendAmin 就執(zhí)行對應(yīng)狀態(tài)下的畫方法和運動算法。
2.巧用策略模式
上述動畫狀態(tài)的抽取,替換,使用了正是策略模式。
定義:策略模式是指對一系列的算法定義,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化。
使用了策略模式后,現(xiàn)在再加一個動畫狀態(tài)此時顯得非常簡單,比如在FinishAnim狀態(tài)之后,還想執(zhí)行一個Clear狀態(tài)的動畫,那么定義一個ClearAnim繼承于ClearViewAnim。
mViewAnim = new FinishAnim();替換成mViewAnim = new ClearAnim();

無論擴展多種狀態(tài),只需要新建一個狀態(tài)Anim繼承于狀態(tài)抽象類,而無需對onDraw進行改動,這滿足了程序設(shè)計的一條重要原則"對修改關(guān)閉、對擴展開放"
3.優(yōu)化
4.工程源碼
本文沒有對具體的運行動畫展開,主要都是一些數(shù)學(xué)計算,工程已托管在github上,歡迎start!
工程源碼