Facebook rebond 彈簧功能源碼分析

相信大家在項(xiàng)目上或多或少的用到了彈簧功能,比較常用和方便的就是facebook的開源庫rebond,想要深入的了解這個(gè)開源庫的使用方法就需要我們源碼分析(read the fucking source codeA龆谩)整個(gè)開源庫的功能實(shí)現(xiàn)牢酵。我自己也通過rebond寫了個(gè)demo 有興趣的可以下載了解下(代碼都有注釋 適合菜鳥初學(xué)者了解 大神略過)

廢話不多說四康,下面我們就開始分析源代碼(very fucking detailed9怪邸)

(一)rebond的配置

在gradle 進(jìn)行配置:

dependencies {
compile 'com.facebook.rebound:rebound:0.3.8'
}
 

或者maven部署rebond庫

<dependency>
  <groupId>com.facebook.rebound</groupId>
  <artifactId>rebound</artifactId>
  <version>0.3.8</version>
</dependency>

這時(shí)候我們可以看到rebond的目錄結(jié)構(gòu):

|-- ui
| |-- utils (layout的工具類 提供create四個(gè)不同參數(shù)的layout布局 例如:寬高自適應(yīng)父類布局等)
| |-- SpringConfiguratorView(彈簧(Spring) view 配置器Spring 配置信息 包含Spring張力值0-200 摩擦力0-50)
|-- AndroidSpringLooperFactory

|-- AnimationQueue
|-- BaseSpringSystem
|-- BuildConfig (包含了開源庫rebond的版本信息等)

|-- ChoreographerCompat

|-- OrigamiValueConverter

|-- SimpleSpringListener (實(shí)現(xiàn)了SpringLsitener接口)

|-- Spring

|-- SpringChain

|-- SpringConfig

|-- SpringConfigRegistry

|-- SpringListener (包含Spring 四個(gè)運(yùn)動(dòng)狀態(tài)的方法)

|-- SpringLooper

|-- SpringSystem

|-- SpringSystemListener

|-- SpringUtil

|-- SteppingLooper

|-- SynchronousLooper

整體的結(jié)構(gòu)就是這樣的枫匾,下面我們來詳細(xì)的介紹下每個(gè)類的作用和功能 以及rebond是怎么運(yùn)作的

(二) rebond的每個(gè)類的作用

  • ui文件夾下utils:

這個(gè)不用多說 此類提供創(chuàng)建四種視圖布局分別為

createMatchParams();
createWrapParams();
createWrapMatchParams();
createMatchWrapParams();
這個(gè)類的作用也就是demo中給定的動(dòng)畫展示所用到的視圖適配架诞。

  • ui文件夾下SpringConfiguratorView(可省略)

    這個(gè)我仔細(xì)看了下 發(fā)現(xiàn)可能是原demo有個(gè)手動(dòng)滑動(dòng)設(shè)置張力摩擦力的seekbar 這里不需要用到 所以忽略掉

    然而重要的是我們要清楚在Spring中,彈簧的運(yùn)動(dòng)軌跡遵循胡克定律 要我們的彈簧能夠彈起來需要我們手動(dòng)設(shè)置張力(TENSION)和摩擦力(FRICTION)

  • SpringConfig

這里是設(shè)置彈簧張力和拉力的類干茉,類中聲明Spring的張力和摩擦力谴忧,在這個(gè)類中我們可以通過調(diào)用

fromOrigamiTensionAndFriction(Tension,Friction)

方法來設(shè)置我們彈簧的張力和拉力,而這會(huì)return一個(gè)參數(shù)經(jīng)過OrigamiValueConverter轉(zhuǎn)換的SpringConfig對象,假如你想用默認(rèn)的參數(shù)也可以通過直接調(diào)用暴露給的靜態(tài)方法
SpringConfig.defaultConfig沾谓,返回的是<code>fromOrigamiTensionAndFriction(40.0D, 7.0D)</code> 既默認(rèn)40 70

  • SpringConfigRegistry

放置大批量SpringConfig的類庫委造。在SpringConfigRegistry中 聲明了一個(gè)SpringConfig的map集合,主要的作用就是add和remove SpringConfig 在下文SpringChain會(huì)提到

  • OrigamiValueConverter

在SpringConfig中提到均驶,設(shè)置張力和摩擦力經(jīng)過此類進(jìn)行一個(gè)轉(zhuǎn)換昏兆,
這里代碼如下:

public OrigamiValueConverter() {
}

public static double tensionFromOrigamiValue(double oValue) {
    return oValue == 0.0D?0.0D:(oValue - 30.0D) * 3.62D + 194.0D;
}

public static double origamiValueFromTension(double tension) {
    return tension == 0.0D?0.0D:(tension - 194.0D) / 3.62D + 30.0D;
}

public static double frictionFromOrigamiValue(double oValue) {
    return oValue == 0.0D?0.0D:(oValue - 8.0D) * 3.0D + 25.0D;
}

public static double origamiValueFromFriction(double friction) {
    return friction
  • SpringSystem

SpringSystem 繼承于BaseSpringSystem 內(nèi)部隱藏了它的構(gòu)造方法,我的需要使用它的靜態(tài)方法create() 妇穴,這個(gè)方法自動(dòng)為我們創(chuàng)建了一個(gè)SpringLooper爬虱,我們來看看這個(gè)方法返回的數(shù)據(jù)<code> return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());</code>
這也是我們在使用彈簧功能前提,我們需要調(diào)用這個(gè)方法得到一個(gè)彈簧腾它,之后再對這個(gè)彈簧進(jìn)行基本的設(shè)置

  • AndroidSpringLooperFactory

在SpringSystem 我們提到跑筝,使用rebond時(shí),自動(dòng)為我們creat一個(gè)SpringLooper瞒滴,此create方法根據(jù)API是否>16(4.0) 為界限曲梗,自動(dòng)返回不同的AndroidSpringLooper實(shí)例,不同的類又共同繼承SpringLooper逛腿,也就是說 稀并,在這個(gè)類中,主要的作用就是根據(jù)api不同創(chuàng)建不同的AndroidSpringLooper對不同版本進(jìn)行適配单默,AndroidSpringLooper 也就是looper碘举,調(diào)用的是BaseSpringSystem的 對looper進(jìn)行的迭代計(jì)算器,做的就是不斷的更新SpringSystem的狀態(tài)搁廓。

  • SpringLooper

上面說到以4.0為界限分別返回繼承SpringLooper的不同的實(shí)例引颈,在SpringLooper這個(gè)抽象類中,有兩個(gè)抽象方法start()和stop()境蜕,子類根據(jù)自身代碼來操作Looper的開始和結(jié)束需要做的事情蝙场,其實(shí)主要是調(diào)用了BaseSpringSystem的loop方法。

  • BaseSpringSystem

在BaseSpringSystem中維護(hù)了一個(gè)SpringSystemListener數(shù)組粱年,可以進(jìn)行addListener或者removelistner的操作售滤, 并且此類提供了對Spring的注冊及初始化,對彈簧運(yùn)動(dòng)進(jìn)行迭代計(jì)算台诗,以及l(fā)oop的迭代計(jì)算完箩,可以說 這個(gè)類是維護(hù)彈簧持續(xù)運(yùn)動(dòng)計(jì)算的一個(gè)類 ,概括來說 這個(gè)類為我們創(chuàng)建了一個(gè)彈簧該有的東西拉队,彈簧的運(yùn)動(dòng)監(jiān)聽弊知,彈簧的物理運(yùn)動(dòng), 主要代碼如下:

   //loop的迭代計(jì)算
   public void loop(double ellapsedMillis) {
       Iterator i$ = this.mListeners.iterator();

       SpringSystemListener listener;
       while(i$.hasNext()) {
           listener = (SpringSystemListener)i$.next();
           listener.onBeforeIntegrate(this);
       }

       this.advance(ellapsedMillis);
       if(this.mActiveSprings.isEmpty()) {
           this.mIdle = true;
       }

       i$ = this.mListeners.iterator();

       while(i$.hasNext()) {
           listener = (SpringSystemListener)i$.next();
           listener.onAfterIntegrate(this);
       }

       if(this.mIdle) {
           this.mSpringLooper.stop();
       }
       
       // Spring 物理運(yùn)動(dòng)計(jì)算
 void advance(double deltaTime) {
       Iterator i$ = this.mActiveSprings.iterator();
       while(i$.hasNext()) {
           Spring spring = (Spring)i$.next();
           if(spring.systemShouldAdvance()) {
               spring.advance(deltaTime / 1000.0D);
           } else {
               this.mActiveSprings.remove(spring);
           }

  • Spring

當(dāng)當(dāng)當(dāng)當(dāng)~ 這就是我們的彈簧啦粱快,在這個(gè)類中詳細(xì)的計(jì)算彈簧運(yùn)動(dòng)的物理計(jì)算 :代碼有點(diǎn)多秩彤。我們可以詳細(xì)的了解下彈簧的運(yùn)動(dòng)過程:

void advance(double realDeltaTime) {
       boolean isAtRest = this.isAtRest();
       if(!isAtRest || !this.mWasAtRest) {
           double adjustedDeltaTime = realDeltaTime;
           if(realDeltaTime > 0.064D) {
               adjustedDeltaTime = 0.064D;
           }

           this.mTimeAccumulator += adjustedDeltaTime;
           double tension = this.mSpringConfig.tension;
           double friction = this.mSpringConfig.friction;
           double position = this.mCurrentState.position;
           double velocity = this.mCurrentState.velocity;
           double tempPosition = this.mTempState.position;

           double dvdt;
           double tempVelocity;
           for(tempVelocity = this.mTempState.velocity; this.mTimeAccumulator >= 0.001D; velocity += dvdt * 0.001D) {
               this.mTimeAccumulator -= 0.001D;
               if(this.mTimeAccumulator < 0.001D) {
                   this.mPreviousState.position = position;
                   this.mPreviousState.velocity = velocity;
               }

               double aAcceleration = tension * (this.mEndValue - tempPosition) - friction * velocity;
               tempPosition = position + velocity * 0.001D * 0.5D;
               tempVelocity = velocity + aAcceleration * 0.001D * 0.5D;
               double bVelocity = tempVelocity;
               double bAcceleration = tension * (this.mEndValue - tempPosition) - friction * tempVelocity;
               tempPosition = position + tempVelocity * 0.001D * 0.5D;
               tempVelocity = velocity + bAcceleration * 0.001D * 0.5D;
               double cVelocity = tempVelocity;
               double cAcceleration = tension * (this.mEndValue - tempPosition) - friction * tempVelocity;
               tempPosition = position + tempVelocity * 0.001D;
               tempVelocity = velocity + cAcceleration * 0.001D;
               double dAcceleration = tension * (this.mEndValue - tempPosition) - friction * tempVelocity;
               double dxdt = 0.16666666666666666D * (velocity + 2.0D * (bVelocity + cVelocity) + tempVelocity);
               dvdt = 0.16666666666666666D * (aAcceleration + 2.0D * (bAcceleration + cAcceleration) + dAcceleration);
               position += dxdt * 0.001D;
           }

           this.mTempState.position = tempPosition;
           this.mTempState.velocity = tempVelocity;
           this.mCurrentState.position = position;
           this.mCurrentState.velocity = velocity;
           if(this.mTimeAccumulator > 0.0D) {
               this.interpolate(this.mTimeAccumulator / 0.001D);
           }

           if(this.isAtRest() || this.mOvershootClampingEnabled && this.isOvershooting()) {
               if(tension > 0.0D) {
                   this.mStartValue = this.mEndValue;
                   this.mCurrentState.position = this.mEndValue;
               } else {
                   this.mEndValue = this.mCurrentState.position;
                   this.mStartValue = this.mEndValue;
               }

               this.setVelocity(0.0D);
               isAtRest = true;
           }

           boolean notifyActivate = false;
           if(this.mWasAtRest) {
               this.mWasAtRest = false;
               notifyActivate = true;
           }

           boolean notifyAtRest = false;
           if(isAtRest) {
               this.mWasAtRest = true;
               notifyAtRest = true;
           }

           Iterator i$ = this.mListeners.iterator();

           while(i$.hasNext()) {
               SpringListener listener = (SpringListener)i$.next();
               if(notifyActivate) {
                   listener.onSpringActivate(this);
               }

               listener.onSpringUpdate(this);
               if(notifyAtRest) {
                   listener.onSpringAtRest(this);
               }
           }

       }
   }

同時(shí)叔扼,創(chuàng)建一個(gè)Spring需要調(diào)用SpringSystem的createSpring( )方法。
這里面詳細(xì)的定義了彈簧運(yùn)動(dòng)的各種東西漫雷,比如詳細(xì)的記錄彈簧運(yùn)動(dòng)到某個(gè)階段的值(彈簧運(yùn)動(dòng)的物理狀態(tài))瓜富, 運(yùn)動(dòng)到某個(gè)階段的彈簧的長度等等 。

  • ChoreographerCompat(可省略)

貌似是舞蹈者 舞蹈者就是控制圖形動(dòng)畫和ui的類 詳細(xì)可以看這篇文章珊拼,這里詳細(xì)的介紹android舞蹈者的作用
這個(gè)類根據(jù)Api是否》=16 (4.0) 控制不同api延遲或者立即 post和remove Choreographer的Callback 這里的運(yùn)用貌似是在AnimationQuee中用到食呻,但是AnimationQuee 在實(shí)際的代碼中也并未用到流炕,所以這里可以省略不談 看別人說好像用AnimationQuee應(yīng)該是有什么坑澎现,我覺得應(yīng)該是適配的坑,每辟,剑辫。AnimationQuee的介紹也省略

  • SpringChain

SpringChain 顧名思義,Spring連鎖(也就是多個(gè)Spring的連鎖)渠欺。如果你想多個(gè)view設(shè)置彈簧功能的需求妹蔽,就可以用到SpringChain,SpringChain會(huì)從第一個(gè)圖片開始一個(gè)一個(gè)得帶動(dòng)下一個(gè)圖片的運(yùn)動(dòng)(如果是單個(gè)的話用Spring就可以)挠将,在這個(gè)類里胳岂,給我們提供了一個(gè)oncreat()的靜態(tài)方法供我們使用,參數(shù)依次為主拉力舔稀,主摩擦力乳丰,輔助拉力,輔助摩擦力内贮,之后我們給每個(gè)view通過springChain.addSpring添加到隊(duì)列中产园,并且設(shè)置SpringListener,最后通過springChain.setControlSpringIndex(0).getControlSpring().setEndValue(0);設(shè)置剛開始的彈簧的index 比如一個(gè)4個(gè)view 第一個(gè)先動(dòng)的是4 那么最后一個(gè)就是0 讓我們來看看具體的代碼:


/**
  * 將一個(gè)彈簧添加到將返回給所提供偵聽器的鏈中夜郁。
  * @param  監(jiān)聽SpringChain中的Spring 并且通知更新它
  * @return this SpringChain for chaining(返回SpringChain的鏈接)
  */
 public SpringChain addSpring(final SpringListener listener) {
   // We listen to each spring added to the SpringChain and dynamically chain the springs together
   // whenever the control spring state is modified.
   Spring spring = mSpringSystem
       .createSpring()
       .addListener(this)
       .setSpringConfig(mAttachmentSpringConfig);
   mSprings.add(spring);
   mListeners.add(listener);
   return this;
 }

 /**
/ /設(shè)置控制彈簧的索引什燕。此彈簧將帶動(dòng)所有彈簧的位置進(jìn)行運(yùn)動(dòng)
  * Set the index of the control spring. This spring will drive the positions of all the springs
  * before and after it in the list when moved.
  * @param i the index to use for the control spring(指針i 用于控制彈簧)
  * @return this SpringChain
  */
 public SpringChain setControlSpringIndex(int i) {
   mControlSpringIndex = i;
   Spring controlSpring = mSprings.get(mControlSpringIndex);
   if (controlSpring == null) {
     return null;
   }
   for (Spring spring : mSpringSystem.getAllSprings()) {
     spring.setSpringConfig(mAttachmentSpringConfig);
   }
   getControlSpring().setSpringConfig(mMainSpringConfig);
   return this;
 }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市竞端,隨后出現(xiàn)的幾起案子屎即,更是在濱河造成了極大的恐慌,老刑警劉巖事富,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件技俐,死亡現(xiàn)場離奇詭異,居然都是意外死亡赵颅,警方通過查閱死者的電腦和手機(jī)虽另,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饺谬,“玉大人捂刺,你說我怎么就攤上這事谣拣。” “怎么了族展?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵森缠,是天一觀的道長。 經(jīng)常有香客問我仪缸,道長贵涵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任恰画,我火速辦了婚禮宾茂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拴还。我一直安慰自己跨晴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布片林。 她就那樣靜靜地躺著端盆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪费封。 梳的紋絲不亂的頭發(fā)上焕妙,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音弓摘,去河邊找鬼焚鹊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衣盾,可吹牛的內(nèi)容都是我干的寺旺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼势决,長吁一口氣:“原來是場噩夢啊……” “哼阻塑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起果复,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤陈莽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虽抄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體走搁,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年迈窟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了私植。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡车酣,死狀恐怖曲稼,靈堂內(nèi)的尸體忽然破棺而出索绪,到底是詐尸還是另有隱情,我是刑警寧澤贫悄,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布瑞驱,位于F島的核電站,受9級特大地震影響窄坦,放射性物質(zhì)發(fā)生泄漏唤反。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一鸭津、第九天 我趴在偏房一處隱蔽的房頂上張望彤侍。 院中可真熱鬧,春花似錦曙博、人聲如沸拥刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吴汪,卻和暖如春惠窄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漾橙。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工杆融, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霜运。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓脾歇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淘捡。 傳聞我的和親對象是個(gè)殘疾皇子藕各,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)焦除,斷路器激况,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,803評論 6 342
  • 第108個(gè)大叔閱讀 161評論 0 0
  • 文/07 翱翔雄鷹任憑空 羨得懦人嘆無究 余生偷得荒寂處 誤了年少壯志雄 17.12.10.中午閑作
    723edf844d12閱讀 163評論 5 23
  • 今天看到支教同事發(fā)在朋友圈的照片,忽然有點(diǎn)懷念在泰國的日子膘魄,那段近似閉關(guān)修行的難忘經(jīng)歷乌逐。 離開坦亞布里皇家理工大學(xué)...
    嫘妮閱讀 372評論 4 3