Android基础-Facebook Rebound 弹性动画库 源码分析

Facebook Rebound 弹性动画库 源码分析

设计的时候老是闲动画太生硬,于是找到了这个弹性动画。这个弹性动画是facebook开源的,Rebound项目地址:https://github.com/facebook/rebound.git。来看下开源的DEMO提供效果。

使用方法

publicclass MainActivity extendsActivity {

privatefinal BaseSpringSystem mSpringSystem = SpringSystem.create();
privatefinal ExampleSpringListener mSpringListener = new ExampleSpringListener();
private FrameLayout mRootView;
private Spring mScaleSpring;
private View mImageView;

@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    mRootView = (FrameLayout) findViewById(R.id.root_view);
    mImageView = mRootView.findViewById(R.id.image_view);

// Create the animation spring.
    mScaleSpring = mSpringSystem.createSpring();

// Add an OnTouchListener to the root view.
    mRootView.setOnTouchListener(new View.OnTouchListener() {
@Override
publicbooleanonTouch(View v, MotionEvent event){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// When pressed start solving the spring to 1.
            mScaleSpring.setEndValue(1);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// When released start solving the spring to 0.
            mScaleSpring.setEndValue(0);
break;
        }
returntrue;
      }
    });
  }

@Override
public void onResume(){
super.onResume();
// Add a listener to the spring when the Activity resumes.
    mScaleSpring.addListener(mSpringListener);
  }

@Override
publicvoidonPause(){
super.onPause();
// Remove the listener to the spring when the Activity pauses.
    mScaleSpring.removeListener(mSpringListener);
  }

privateclass ExampleSpringListener extendsSimpleSpringListener {
@Override
publicvoidonSpringUpdate(Spring spring){
// On each update of the spring value, we adjust the scale of the image view to match the
// springs new value. We use the SpringUtil linear interpolation function mapValueFromRangeToRange
// to translate the spring's 0 to 1 scale to a 100% to 50% scale range and apply that to the View
// with setScaleX/Y. Note that rendering is an implementation detail of the application and not
// Rebound itself. If you need Gingerbread compatibility consider using NineOldAndroids to update
// your view properties in a backwards compatible manner.
float mappedValue = (float) SpringUtil.mapValueFromRangeToRange(spring.getCurrentValue(), 0, 1, 1, 0.5);
      mImageView.setScaleX(mappedValue);
      mImageView.setScaleY(mappedValue);
    }
  }

}

源码解析

1.输出值分析

public void onSpringUpdate(Spring spring){

Log.v("tag" , spring.getCurrentValue())

}

输出值 主要看这里 我们将 回调的输出值输入Excle表格 在做成图表。可以看出,如果将这个值放入 位移变化 很明显可以形成一个弹性的小球的样子。大家可以想象。

2.触发而触发输出值,看如代码。


case MotionEvent.ACTION_DOWN:

// When pressed start solving the spring to 1.

mScaleSpring.setEndValue(1);

break;

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

// When released start solving the spring to 0.

mScaleSpring.setEndValue(0);

break;

在由上面的代码,看出触发回调的是 mScaleSpring.setEndValue(1);

我们将更近代码,进入Spring类,

public Spring setEndValue(double endValue){

if (mEndValue == endValue && isAtRest()) {

    return this;

}

mStartValue = getCurrentValue();

mEndValue = endValue;

mSpringSystem.activateSpring(this.getId());

for (SpringListener listener : mListeners) {

listener.onSpringEndStateChange(this);

}

    return this;

}

这里执行一些回调,主要看 mSpringSystem.activateSpring(this.getId());

void activateSpring(String springId) {

Spring spring = mSpringRegistry.get(springId);

if (spring == null) {

thrownew IllegalArgumentException("springId " + springId + " does not reference a registered spring");

}

mActiveSprings.add(spring);

if (getIsIdle()) {

mIdle = false;

mSpringLooper.start();

}

}

这里 SpringSystem将当前的spring取出 加入到mActiveSprings列表中,然后mSpringLooper.start(); 这里mSpringLooper 你可以先想象成一个简单的loop循环。在这个loop就开始疯狂的输出算法每隔一段时间的值了。我们找到这个start的实现就一清二楚了。当然loop是怎么生成的我们要看下SpringSystem的构造,

3.SpringSystem 和loop的形成

public class SpringSystem extendsBaseSpringSystem {



/**

* Create a new SpringSystem providing the appropriate constructor parameters to work properly

* in an Android environment.

* @return the SpringSystem

*/

public static SpringSystem create() {

returnnew SpringSystem(AndroidSpringLooperFactory.createSpringLooper());

}



private SpringSystem(SpringLooper springLooper) {

super(springLooper);

}



}

瞧loop就是它了AndroidSpringLooperFactory.createSpringLooper()跟进。

publicstaticSpringLooper createSpringLooper(){

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

return ChoreographerAndroidSpringLooper.create();

} else {

return LegacyAndroidSpringLooper.create();

}

}

我们选择的是 

LegacyAndroidSpringLooper.create();

publicLegacyAndroidSpringLooper(Handler handler){

mHandler = handler;

mLooperRunnable = new Runnable() {

@Override

publicvoidrun(){

if (!mStarted || mSpringSystem == null) {

return;

}

long currentTime = SystemClock.uptimeMillis();

mSpringSystem.loop(currentTime - mLastTime);

mLastTime = currentTime;

mHandler.post(mLooperRunnable);

}

};

}



@Override

publicvoidstart(){

if (mStarted) {

return;

}

mStarted = true;

mLastTime = SystemClock.uptimeMillis();

mHandler.removeCallbacks(mLooperRunnable);

mHandler.post(mLooperRunnable);

}



@Override

publicvoidstop(){

mStarted = false;

mHandler.removeCallbacks(mLooperRunnable);

}

这里就 循环的post,如果没有stop的话,就是个死循环了。而循环里执行的内如无非是,mSpringSystem.loop(currentTime - mLastTime); 那么我们回到SpringSystem 看下 loop函数

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

}

}

好家伙这里也什么都没有做 ,也只是一些判断和回调 但是这里的判断可 重要了 这里判断了循环停止的条件– 在最后面(等下分析)。 这里主要看下advance(elapsedMillis);这个算法。

voidadvance(double deltaTime){

for (Spring spring : mActiveSprings) {

// advance time in seconds

if (spring.systemShouldAdvance()) {

spring.advance(deltaTime / 1000.0);

} else {

mActiveSprings.remove(spring);

}

}

}

这样整个系统就一目了然了 ,简单的概括过程如下

  • 建立loop循环 然后一直post
  • 然后SpringSystem 调用每个spring.advance(deltaTime / 1000.0);输出算法值。
  • 停止 什么时候停止。mActiveSprings.remove(spring);移除到empty的时候。什么时候移除要看 spring.systemShouldAdvance()

3.主要算法解析

void advance(double realDeltaTime) {



boolean isAtRest = isAtRest();



if (isAtRest && mWasAtRest) {

return;

}

double adjustedDeltaTime = realDeltaTime;

if (realDeltaTime > MAX_DELTA_TIME_SEC) {

adjustedDeltaTime = MAX_DELTA_TIME_SEC;

}



mTimeAccumulator += adjustedDeltaTime;



double tension = mSpringConfig.tension;

double friction = mSpringConfig.friction;



double position = mCurrentState.position;

double velocity = mCurrentState.velocity;

double tempPosition = mTempState.position;

double tempVelocity = mTempState.velocity;



double aVelocity, aAcceleration;

double bVelocity, bAcceleration;

double cVelocity, cAcceleration;

double dVelocity, dAcceleration;

double dxdt, dvdt;

while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {

mTimeAccumulator -= SOLVER_TIMESTEP_SEC;

if (mTimeAccumulator < SOLVER_TIMESTEP_SEC) {

mPreviousState.position = position;

mPreviousState.velocity = velocity;

}

aVelocity = velocity;

aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity;

tempPosition = position + aVelocity * SOLVER_TIMESTEP_SEC * 0.5;

tempVelocity = velocity + aAcceleration * SOLVER_TIMESTEP_SEC * 0.5;

bVelocity = tempVelocity;

bAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;



tempPosition = position + bVelocity * SOLVER_TIMESTEP_SEC * 0.5;

tempVelocity = velocity + bAcceleration * SOLVER_TIMESTEP_SEC * 0.5;

cVelocity = tempVelocity;

cAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;



tempPosition = position + cVelocity * SOLVER_TIMESTEP_SEC;

tempVelocity = velocity + cAcceleration * SOLVER_TIMESTEP_SEC;

dVelocity = tempVelocity;

dAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

dxdt = 1.0/6.0 * (aVelocity + 2.0 * (bVelocity + cVelocity) + dVelocity);

dvdt = 1.0/6.0 * (aAcceleration + 2.0 * (bAcceleration + cAcceleration) + dAcceleration);



position += dxdt * SOLVER_TIMESTEP_SEC;

velocity += dvdt * SOLVER_TIMESTEP_SEC;

}

mTempState.position = tempPosition;

mTempState.velocity = tempVelocity;



mCurrentState.position = position;

mCurrentState.velocity = velocity;

...

我们先简化下上面的算法,提取我们需要的值,主要是想知道第1步中得到的值是怎么改变的

publicdoublegetCurrentValue(){

return mCurrentState.position;

}

我们看到 在倒数第二行改变了, mCurrentState.position = position; 那么按到position 是如何被赋值的 position += dxdt * SOLVER_TIMESTEP_SEC;SOLVER_TIMESTEP_SEC是个常亮量,dxdt 这个是个平均值 dxdt = 1.0/6.0 * (aVelocity + 2.0 * (bVelocity + cVelocity) + dVelocity); 那么我想如果不用平均值怎么样,我让dxdt = aVelocity ;重新启动demo 也是可以的。我们就讲b,c,d 全删除了,算法就等同下面的了。

aVelocity = velocity;

aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity;

tempPosition = position + aVelocity * SOLVER_TIMESTEP_SEC * 0.5;

tempVelocity = velocity + aAcceleration * SOLVER_TIMESTEP_SEC * 0.5;

dxdt = aVelocity;

position += dxdt * SOLVER_TIMESTEP_SEC;

这里大概就是在模拟弹力公式

F = k△x - F阻

4.总结

  • 使用起来方便 设置下阻力和弹性系数就可以得到需要的弹性值
  • 可以将弹性公式扩展成其他公式,以满足其他的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈万三djh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值