Facebook Rebound 彈性動畫庫 源碼分析

Rebound源碼分析

讓動畫再也不僵硬:Facebook Rebound Android動畫庫介紹一文中介紹了rebound這個庫。java

對於想體驗一下rebound的效果,又懶得clone和編譯代碼的,這裏提供一個demo apkgit

今天看到了tumblr發佈了基於reboundBackboard,本想直接分析一下Backboard對rebound作了些什麼,不過考慮到rebound尚未仔細分析過,因此這裏作一下源碼分析。github

對外部來講,首先接觸的就是SpringSystem了,但在說它以前,先讓咱們看看spring是什麼。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從mStartValuemEndValue進行運動,內部維護了當前狀態、前值狀態,以及臨時狀態,每一個狀態由經過位置和速度來描述,而運動的推動邏輯則在ide

void advance(double realDeltaTime)
  • 1

advance方法中,SpringSystem會遍歷由其管理的全部Spring實例,對它們進行advanceoop

SpringListener

每一個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的狀態,實際調用了BaseSpringSystemloop方法:動畫

/** * 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;
  • 1

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); }

會在SpringSystemloop方法開始和結束時候調用onBeforeIntegrate以及onAfterIntegrate,好比能夠在全部Spring loop完以後檢查它們的值,並進行速度限制,暫停等操做,相對於綁定到SpringSpringListener,這個更全局一些。

SpringChain

顧名思義,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++); }
  • 1
  • 2

即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); }
  • 1

一般咱們想要這個SpringChain進行運動會調用mSpringChain.setControlSpringIndex(0).getControlSpring().setEndValue(1);

ControlSpring便會開始運動,並調用到SpringChain做爲SpringListener的那些方法,進而整個系統做爲一個鏈開始運動。

SpringConfiguratorView

SpringConfiguratorView繼承了FrameLayout,若是體驗過demo apk的同窗,應該注意到屏幕底下上拉能夠對Spring的參數進行配置,這就是由SpringConfiguratorView作的了。

AnimationQueue

一樣是用來作連鎖動畫的,不過Backboard沒有用到這個,Facebook本身的例子也沒有用過該類,之前作動畫的時候用過這個,結果貌似是有什麼坑,最後改爲了SpringChain去實現。

AnimationQueue自己和Rebound沒有任何關係,內部定義了接口

public interface Callback { void onFrame(Double value); }

 

原理卻是有點像rebound。因爲和rebound自己不要緊,這裏就很少說了。

相關文章
相關標籤/搜索