讓動畫再也不僵硬:Facebook Rebound Android動畫庫介紹一文中介紹了rebound這個庫。java
對於想體驗一下rebound的效果,又懶得clone和編譯代碼的,這裏提供一個demo apk。git
今天看到了tumblr發佈了基於rebound的Backboard,本想直接分析一下Backboard對rebound作了些什麼,不過考慮到rebound尚未仔細分析過,因此這裏作一下源碼分析。github
對外部來講,首先接觸的就是SpringSystem
了,但在說它以前,先讓咱們看看spring
是什麼。spring
Spring經過可設置的摩擦力(Friction)和張力(tension)實現了胡克定律,經過代碼模擬了物理場景:數組
private static class PhysicsState { double position; double velocity; } private final PhysicsState mCurrentState = new PhysicsState(); private final PhysicsState mPreviousState = new PhysicsState(); private final PhysicsState mTempState = new PhysicsState(); private double mStartValue; private double mEndValue;
每一個spring從mStartValue
到mEndValue
進行運動,內部維護了當前狀態、前值狀態,以及臨時狀態,每一個狀態由經過位置和速度來描述,而運動的推動邏輯則在ide
void advance(double realDeltaTime)
advance
方法中,SpringSystem會遍歷由其管理的全部Spring實例,對它們進行advance
。oop
每一個Spring
內部都維護着一個SpringListener
數組,這也是咱們常常會須要去實現的一個接口:源碼分析
public interface SpringListener { void onSpringUpdate(Spring spring); void onSpringAtRest(Spring spring); void onSpringActivate(Spring spring); void onSpringEndStateChange(Spring spring); }
能夠看到create方法裏面默認給了一個SpringLooper
的工廠類建立實例(內部根據系統版本是否>=3.0返回了不一樣的子類實例),而SpringLooper
顧名思義是一個Looper,作的就是不斷地更新SpringSystem
的狀態,實際調用了BaseSpringSystem
的loop
方法:動畫
/** * loop the system until idle * @param elapsedMillis elapsed milliseconds */ public void loop(double elapsedMillis) { for (SpringSystemListener listener : mListeners) { listener.onBeforeIntegrate(this); } advance(elapsedMillis); if (mActiveSprings.isEmpty()) { mIdle = true; } for (SpringSystemListener listener : mListeners) { listener.onAfterIntegrate(this); } if (mIdle) { mSpringLooper.stop(); } }
即經過每次elapse的時間,來把system往前advance(有點相似遊戲裏,每一幀的運動,若是不夠快就會掉幀,這裏對應地,elapsedMillis則可能會很大)。this
大部分的邏輯其實在BaseSpringSystem
:
public class BaseSpringSystem { private final Map<String, Spring> mSpringRegistry = new HashMap<String, Spring>(); private final Set<Spring> mActiveSprings = new CopyOnWriteArraySet<Spring>(); private final SpringLooper mSpringLooper; private final CopyOnWriteArraySet<SpringSystemListener> mListeners = new CopyOnWriteArraySet<SpringSystemListener>(); private boolean mIdle = true;
mSpringRegistry
保存了全部由該SpringSystem
管理的Spring
實例,鍵值String則是Spring內的一個自增id,每一個Spring
實例的id都會不一樣。經過createSpring
建立的Spring
實例都會直接被加到該HashMap。
mActiveSprings
內放的是被激活的Spring
,實際在調用Spring.Java
:
public Spring setCurrentValue(double currentValue, boolean setAtRest); public Spring setEndValue(double endValue); public Spring setVelocity(double velocity);
三個方法的時候纔會進行激活,且在實際loop過程當中,也只會對激活的Spring進行advance。
mSpringLooper
是該SpringSystem
綁定的Looper。
mListeners
是註冊在該SpringSystem
上的SpringSystemListener
public interface SpringSystemListener { void onBeforeIntegrate(BaseSpringSystem springSystem); void onAfterIntegrate(BaseSpringSystem springSystem); }
會在SpringSystem
的loop
方法開始和結束時候調用onBeforeIntegrate
以及onAfterIntegrate
,好比能夠在全部Spring loop完以後檢查它們的值,並進行速度限制,暫停等操做,相對於綁定到Spring
的SpringListener
,這個更全局一些。
顧名思義,SpringChain
就是連鎖Spring,由數個Spring
結合而成,且兩兩相連,能夠用來作一些連鎖的效果,好比數個圖片之間的牽引效果。
每一個SpringChain
都會有一個control spring來做爲帶頭大哥,在鏈中先後的Spring
都會被他們的前任所拉動。好比咱們有 1 2 3 4 5五個Spring,選擇3做爲帶頭大哥,則3開始運動後,會分別拉動2和4,而後2會拉1,4則去拉動5。
private SpringChain( int mainTension, int mainFriction, int attachmentTension, int attachmentFriction) { mMainSpringConfig = SpringConfig.fromOrigamiTensionAndFriction(mainTension, mainFriction); mAttachmentSpringConfig = SpringConfig.fromOrigamiTensionAndFriction(attachmentTension, attachmentFriction); registry.addSpringConfig(mMainSpringConfig, "main spring " + id++); registry.addSpringConfig(mAttachmentSpringConfig, "attachment spring " + id++); }
即ControlSpring摩擦力和張力都會相對小一些。
SpringChain
自己實現了SpringListener
,並使用那些接口來進行整個chain的更新。
@Override public void onSpringUpdate(Spring spring) { // 得到control spring的索引,並更新先後Spring的endValue,從而觸發連鎖影響 int idx = mSprings.indexOf(spring); SpringListener listener = mListeners.get(idx); int above = -1; int below = -1; if (idx == mControlSpringIndex) { below = idx - 1; above = idx + 1; } else if (idx < mControlSpringIndex) { below = idx - 1; } else if (idx > mControlSpringIndex) { above = idx + 1; } if (above > -1 && above < mSprings.size()) { mSprings.get(above).setEndValue(spring.getCurrentValue()); } if (below > -1 && below < mSprings.size()) { mSprings.get(below).setEndValue(spring.getCurrentValue()); } listener.onSpringUpdate(spring); } @Override public void onSpringAtRest(Spring spring) { int idx = mSprings.indexOf(spring); mListeners.get(idx).onSpringAtRest(spring); } @Override public void onSpringActivate(Spring spring) { int idx = mSprings.indexOf(spring); mListeners.get(idx).onSpringActivate(spring); } @Override public void onSpringEndStateChange(Spring spring) { int idx = mSprings.indexOf(spring); mListeners.get(idx).onSpringEndStateChange(spring); }
一般咱們想要這個SpringChain
進行運動會調用mSpringChain.setControlSpringIndex(0).getControlSpring().setEndValue(1);
ControlSpring便會開始運動,並調用到SpringChain
做爲SpringListener
的那些方法,進而整個系統做爲一個鏈開始運動。
SpringConfiguratorView
繼承了FrameLayout
,若是體驗過demo apk的同窗,應該注意到屏幕底下上拉能夠對Spring的參數進行配置,這就是由SpringConfiguratorView
作的了。
一樣是用來作連鎖動畫的,不過Backboard沒有用到這個,Facebook本身的例子也沒有用過該類,之前作動畫的時候用過這個,結果貌似是有什麼坑,最後改爲了SpringChain去實現。
AnimationQueue自己和Rebound沒有任何關係,內部定義了接口
public interface Callback { void onFrame(Double value); }
原理卻是有點像rebound。因爲和rebound自己不要緊,這裏就很少說了。