Rebound源碼分析
對于想體驗一下rebound的效果关噪,又懶得clone和編譯代碼的鸟蟹,這里提供一個demo apk。
今天看到了tumblr發(fā)布了基于rebound的Backboard使兔,本想直接分析一下Backboard對rebound做了些什么建钥,不過考慮到rebound還沒有仔細分析過,所以這里做一下源碼分析虐沥。
對外部來說熊经,首先接觸的就是SpringSystem
了,但在說它之前置蜀,先讓我們看看Spring
是什么奈搜。
Spring
Spring通過可設置的摩擦力(Friction)和張力(tension)實現(xiàn)了胡克定律,通過代碼模擬了物理場景:
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
進行運動盯荤,內部維護了當前狀態(tài)馋吗、前值狀態(tài),以及臨時狀態(tài)秋秤,每個狀態(tài)由通過位置和速度來描述宏粤,而運動的推進邏輯則在
void advance(double realDeltaTime)
advance
方法中,SpringSystem會遍歷由其管理的所有Spring實例灼卢,對它們進行advance
绍哎。
SpringListener
每個Spring
內部都維護著一個SpringListener
數(shù)組,這也是我們經常會需要去實現(xiàn)的一個接口:
public interface SpringListener {
void onSpringUpdate(Spring spring);
void onSpringAtRest(Spring spring);
void onSpringActivate(Spring spring);
void onSpringEndStateChange(Spring spring);
}
按照先后順序:
- onSpringActivate在首次開始運動時候調用鞋真。
- onSpringUpdate在advance后調用崇堰,表示狀態(tài)更新。
- onSpringAtRest在進入rest狀態(tài)后調用。
- onSpringEndStateChange則略有不同海诲,僅在
setEndValue
中被調用繁莹,且該Spring
需要在運動中且新的endValue不等于原endValue。
SpringSystem
SpringSystem
繼承了BaseSpringSystem
特幔,對外提供了一個靜態(tài)create方法咨演,并屏蔽了Construtor:
public static SpringSystem create() {
return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());
}
private SpringSystem(SpringLooper springLooper) {
super(springLooper);
}
可以看到create方法里面默認給了一個SpringLooper
的工廠類創(chuàng)建實例(內部根據(jù)系統(tǒng)版本是否>=3.0返回了不同的子類實例),而SpringLooper
顧名思義是一個Looper蚯斯,做的就是不斷地更新SpringSystem
的狀態(tài)薄风,實際調用了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則可能會很大)嵌牺。
大部分的邏輯其實在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
創(chuàng)建的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
顧名思義,SpringChain
就是連鎖Spring瓮下,由數(shù)個Spring
結合而成翰铡,且兩兩相連,可以用來做一些連鎖的效果讽坏,比如數(shù)個圖片之間的牽引效果锭魔。
每個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++);
}
SpringChain有兩個配置:
- ControlSpring使用
mMainSpringConfig
膛堤。 - 其他
Spring
則使用mAttachmentSpringConfig
。
在什么參數(shù)都不帶的構造函數(shù)中晌该,會默認給出如下參數(shù)
private static final int DEFAULT_MAIN_TENSION = 40;
private static final int DEFAULT_MAIN_FRICTION = 6;
private static final int DEFAULT_ATTACHMENT_TENSION = 70;
private static final int DEFAULT_ATTACHMENT_FRICTION = 10;
即ControlSpring摩擦力和張力都會相對小一些肥荔。
SpringChain
本身實現(xiàn)了SpringListener
,并使用那些接口來進行整個chain的更新朝群。
@Override
public void onSpringUpdate(Spring spring) {
// 獲得control spring的索引燕耿,并更新前后Spring的endValue,從而觸發(fā)連鎖影響
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
的那些方法誉帅,進而整個系統(tǒng)作為一個鏈開始運動。
SpringConfiguratorView
SpringConfiguratorView
繼承了FrameLayout
右莱,如果體驗過demo apk的同學蚜锨,應該注意到屏幕底下上拉可以對Spring的參數(shù)進行配置,這就是由SpringConfiguratorView
做的了慢蜓。
AnimationQueue
同樣是用來做連鎖動畫的亚再,不過Backboard沒有用到這個,F(xiàn)acebook自己的例子也沒有用過該類晨抡,以前做動畫的時候用過這個氛悬,結果貌似是有什么坑,最后改成了SpringChain去實現(xiàn)耘柱。
AnimationQueue本身和Rebound沒有任何關系如捅,內部定義了接口
public interface Callback {
void onFrame(Double value);
}
原理倒是有點像rebound。由于和rebound本身沒關系调煎,這里就不多說了镜遣。