美文网首页
Facebook rebond 弹簧功能源码分析

Facebook rebond 弹簧功能源码分析

作者: 蒲Annie_ | 来源:发表于2017-09-15 11:59 被阅读69次

相信大家在项目上或多或少的用到了弹簧功能,比较常用和方便的就是facebook的开源库rebond,想要深入的了解这个开源库的使用方法就需要我们源码分析(read the fucking source code!)整个开源库的功能实现。我自己也通过rebond写了个demo 有兴趣的可以下载了解下(代码都有注释 适合菜鸟初学者了解 大神略过)

废话不多说,下面我们就开始分析源代码(very fucking detailed!)

(一)rebond的配置

在gradle 进行配置:

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>

这时候我们可以看到rebond的目录结构:

|-- ui
| |-- utils (layout的工具类 提供create四个不同参数的layout布局 例如:宽高自适应父类布局等)
| |-- SpringConfiguratorView(弹簧(Spring) view 配置器Spring 配置信息 包含Spring张力值0-200 摩擦力0-50)
|-- AndroidSpringLooperFactory

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

|-- ChoreographerCompat

|-- OrigamiValueConverter

|-- SimpleSpringListener (实现了SpringLsitener接口)

|-- Spring

|-- SpringChain

|-- SpringConfig

|-- SpringConfigRegistry

|-- SpringListener (包含Spring 四个运动状态的方法)

|-- SpringLooper

|-- SpringSystem

|-- SpringSystemListener

|-- SpringUtil

|-- SteppingLooper

|-- SynchronousLooper

整体的结构就是这样的,下面我们来详细的介绍下每个类的作用和功能 以及rebond是怎么运作的

(二) rebond的每个类的作用

  • ui文件夹下utils:

这个不用多说 此类提供创建四种视图布局分别为

createMatchParams();
createWrapParams();
createWrapMatchParams();
createMatchWrapParams();
这个类的作用也就是demo中给定的动画展示所用到的视图适配。

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

    这个我仔细看了下 发现可能是原demo有个手动滑动设置张力摩擦力的seekbar 这里不需要用到 所以忽略掉

    然而重要的是我们要清楚在Spring中,弹簧的运动轨迹遵循胡克定律 要我们的弹簧能够弹起来需要我们手动设置张力(TENSION)和摩擦力(FRICTION)

  • SpringConfig

这里是设置弹簧张力和拉力的类,类中声明Spring的张力和摩擦力,在这个类中我们可以通过调用

fromOrigamiTensionAndFriction(Tension,Friction)

方法来设置我们弹簧的张力和拉力,而这会return一个参数经过OrigamiValueConverter转换的SpringConfig对象,假如你想用默认的参数也可以通过直接调用暴露给的静态方法
SpringConfig.defaultConfig,返回的是<code>fromOrigamiTensionAndFriction(40.0D, 7.0D)</code> 既默认40 70

  • SpringConfigRegistry

放置大批量SpringConfig的类库。在SpringConfigRegistry中 声明了一个SpringConfig的map集合,主要的作用就是add和remove SpringConfig 在下文SpringChain会提到

  • OrigamiValueConverter

在SpringConfig中提到,设置张力和摩擦力经过此类进行一个转换,
这里代码如下:

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 内部隐藏了它的构造方法,我的需要使用它的静态方法create() ,这个方法自动为我们创建了一个SpringLooper,我们来看看这个方法返回的数据<code> return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());</code>
这也是我们在使用弹簧功能前提,我们需要调用这个方法得到一个弹簧,之后再对这个弹簧进行基本的设置

  • AndroidSpringLooperFactory

在SpringSystem 我们提到,使用rebond时,自动为我们creat一个SpringLooper,此create方法根据API是否>16(4.0) 为界限,自动返回不同的AndroidSpringLooper实例,不同的类又共同继承SpringLooper,也就是说 ,在这个类中,主要的作用就是根据api不同创建不同的AndroidSpringLooper对不同版本进行适配,AndroidSpringLooper 也就是looper,调用的是BaseSpringSystem的 对looper进行的迭代计算器,做的就是不断的更新SpringSystem的状态。

  • SpringLooper

上面说到以4.0为界限分别返回继承SpringLooper的不同的实例,在SpringLooper这个抽象类中,有两个抽象方法start()和stop(),子类根据自身代码来操作Looper的开始和结束需要做的事情,其实主要是调用了BaseSpringSystem的loop方法。

  • BaseSpringSystem

在BaseSpringSystem中维护了一个SpringSystemListener数组,可以进行addListener或者removelistner的操作, 并且此类提供了对Spring的注册及初始化,对弹簧运动进行迭代计算,以及loop的迭代计算,可以说 这个类是维护弹簧持续运动计算的一个类 ,概括来说 这个类为我们创建了一个弹簧该有的东西,弹簧的运动监听,弹簧的物理运动, 主要代码如下:

   //loop的迭代计算
   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 物理运动计算
 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

当当当当~ 这就是我们的弹簧啦,在这个类中详细的计算弹簧运动的物理计算 :代码有点多。我们可以详细的了解下弹簧的运动过程:

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

       }
   }

同时,创建一个Spring需要调用SpringSystem的createSpring( )方法。
这里面详细的定义了弹簧运动的各种东西,比如详细的记录弹簧运动到某个阶段的值(弹簧运动的物理状态), 运动到某个阶段的弹簧的长度等等 。

  • ChoreographerCompat(可省略)

貌似是舞蹈者 舞蹈者就是控制图形动画和ui的类 详细可以看这篇文章,这里详细的介绍android舞蹈者的作用
这个类根据Api是否》=16 (4.0) 控制不同api延迟或者立即 post和remove Choreographer的Callback 这里的运用貌似是在AnimationQuee中用到,但是AnimationQuee 在实际的代码中也并未用到,所以这里可以省略不谈 看别人说好像用AnimationQuee应该是有什么坑,我觉得应该是适配的坑,,。AnimationQuee的介绍也省略

  • SpringChain

SpringChain 顾名思义,Spring连锁(也就是多个Spring的连锁)。如果你想多个view设置弹簧功能的需求,就可以用到SpringChain,SpringChain会从第一个图片开始一个一个得带动下一个图片的运动(如果是单个的话用Spring就可以),在这个类里,给我们提供了一个oncreat()的静态方法供我们使用,参数依次为主拉力,主摩擦力,辅助拉力,辅助摩擦力,之后我们给每个view通过springChain.addSpring添加到队列中,并且设置SpringListener,最后通过springChain.setControlSpringIndex(0).getControlSpring().setEndValue(0);设置刚开始的弹簧的index 比如一个4个view 第一个先动的是4 那么最后一个就是0 让我们来看看具体的代码:


/**
  * 将一个弹簧添加到将返回给所提供侦听器的链中。
  * @param  监听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;
 }

 /**
/ /设置控制弹簧的索引。此弹簧将带动所有弹簧的位置进行运动
  * 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;
 }

相关文章

网友评论

      本文标题:Facebook rebond 弹簧功能源码分析

      本文链接:https://www.haomeiwen.com/subject/ppessxtx.html