Rebound动画框架简单介绍

时间:2021-09-29 00:13:32

Rebound动画框架简单介绍

Android菜鸟一枚,有不对的地方希望大家指出,谢谢。
最近在接手了一个老项目,发现里面动画框架用的是facebook中的Rebound框架,由于以前没听说过,放假时闲得蛋痛,看看了源码,就顺手写这一篇吧。
写了一个小Demo,具体效果如下:
Rebound动画框架简单介绍
代码很简单,这是xml布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image"
android:src="@drawable/a1" /> </RelativeLayout>

这是MainActivity:

package com.micro.mytest_button;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView; import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringListener;
import com.facebook.rebound.SpringSystem;
import com.nineoldandroids.view.ViewHelper; public class MainActivity extends Activity { private ImageView image;
private Spring spring; private final float mScale = 1.0f; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); image = (ImageView) findViewById(R.id.image); SpringSystem springSystem = SpringSystem.create();
spring = springSystem.createSpring();
spring.addListener(new SpringListener() { @Override
public void onSpringUpdate(Spring spring) {
float value = (float) spring.getCurrentValue();
float scale = 1f - (value * mScale); System.out.println("the value is " + value + "--the scale is --" + scale); ViewHelper.setScaleX(image, scale);
ViewHelper.setScaleY(image, scale);
} @Override
public void onSpringEndStateChange(Spring spring) {
} @Override
public void onSpringAtRest(Spring spring) {
} @Override
public void onSpringActivate(Spring spring) {
}
}); image.setOnTouchListener(new OnTouchListener() { @Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
spring.setEndValue(1.0f);
break; case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
spring.setEndValue(0.0f);
break;
} return true;
}
});
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }

现在主要是来分析下Rebound的源码,看看里面到底怎么走的,用法很简单(这是最简单的用法),就三句话:

SpringSystem springSystem = SpringSystem.create();
spring = springSystem.createSpring();
spring.addListener(new SpringListener() {}
  • 1

主要是创建了三个对象,SpringSystem/spring/SpringListener,对于动画的效果,就是这三个玩意搞出来的。具体的模式如下:
SpringSystem:继承自BaseSpringSystem,其中包含了Spring对象引用的容器,控制Spring对象的操作,存在一个SpringLooper(是一个抽象方法,只有start()/stop()方法),这也是facebook自定义的类,与android.os.Looper无关,只是模拟了android.os.Looper的方法,内存start(),end()方法。其构造方法:

 public static SpringSystem create() {
return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());
}
  • 1

打开SpringSystem(SpringLooper sl)源码:

  public BaseSpringSystem(SpringLooper springLooper) {
if (springLooper == null) {
throw new IllegalArgumentException("springLooper is required");
}
mSpringLooper = springLooper;
mSpringLooper.setSpringSystem(this);
}
  • 1

现在再看AndroidSpringLooperFactory.createSpringLooper()源码:

 public static SpringLooper createSpringLooper() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return ChoreographerAndroidSpringLooper.create();
} else {
return LegacyAndroidSpringLooper.create();
}
}
  • 1

可以看到为了兼容JDK,高低版本创建的SpringLooper容器不经相同,这个不是考虑的重点。

现在来看第二句:

spring = springSystem.createSpring();
  • 1

翻开SpringSytem.createSpring()源码:

 public Spring createSpring() {
Spring spring = new Spring(this);
registerSpring(spring);
return spring;
}
  • 1
  • 2

可以看到Spring类是一个对立的Java类,持有SpringSystem的引用,来看看Spring(SpringSystem ss)构造函数:

  Spring(BaseSpringSystem springSystem) {
if (springSystem == null) {
throw new IllegalArgumentException("Spring cannot be created outside of a BaseSpringSystem");
}
mSpringSystem = springSystem;
mId = "spring:" + ID++;
setSpringConfig(SpringConfig.defaultConfig);
}

再看setSpringConfig(SpringConfig.defaultConfig)源码:

public Spring setSpringConfig(SpringConfig springConfig) {
if (springConfig == null) {
throw new IllegalArgumentException("springConfig is required");
}
mSpringConfig = springConfig;
return this;
}
  • 1

也只是初始化一些参数,并没有有我们想要的源码(这里的意思就是我点击图片时,它为什么会有动画的意思),再看第三个方法吧:

spring.addListener(new SpringListener() {}
  • 1

添加监听器,用的多的人就会有感觉,这个也不会有关键代码可以使我们图片有缩放效果,那么问题来了,看到facebook写的三句话,我们并没有找到动画缩放的想过函数啊,是不是有配置文件或者静态/动态代码块呢??找了很久,没有啊。那只有接下来再看image的代码了,在看到Image的绑定的onTouch事件上,看到了这句话:

    spring.setEndValue(1.0f);
  • 1

啥也不说,进去看看再说吧:

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

其他没啥好看的,看到了这句话:

 mSpringSystem.activateSpring(this.getId());
  • 1

活了二十几年,立马感觉这句话有问题,进入看看:

  void activateSpring(String springId) {
Spring spring = mSpringRegistry.get(springId);
if (spring == null) {
throw new IllegalArgumentException("springId " + springId + " does not reference a registered spring");
}
mActiveSprings.add(spring);
if (getIsIdle()) {
mIdle = false;
mSpringLooper.start();
}
}

解释一下,刚才说的SpringSystem包含了Spring对象的引用,mSpringRegistry.get(springId)是找到SpringSystem中注册的Spring, mActiveSprings.add(spring)是将该spring添加到活动的spring队列中,那现在就看看这句话吧: mSpringLooper.start(),刚才说SpringLooper是一个抽象类,那好随便找个其子类看看里面的方法吧,

private static class LegacyAndroidSpringLooper extends SpringLooper {}
  • 1

刚才的兼容性代码中,JELLY_BEAN(也就是4.1之前的代码),那我们就看看mSpringLooper.start()是个什么鬼了吧:

private static class LegacyAndroidSpringLooper extends SpringLooper {

    private final Handler mHandler;
private final Runnable mLooperRunnable;
private boolean mStarted;
private long mLastTime; /**
* @return an Android spring looper using a new {@link Handler} instance
*/
public static SpringLooper create() {
return new LegacyAndroidSpringLooper(new Handler());
} public LegacyAndroidSpringLooper(Handler handler) {
mHandler = handler;
mLooperRunnable = new Runnable() {
@Override
public void run() {
if (!mStarted || mSpringSystem == null) {
return;
}
long currentTime = SystemClock.uptimeMillis();
mSpringSystem.loop(currentTime - mLastTime);
mHandler.post(mLooperRunnable);
}
};
} @Override
public void start() {
if (mStarted) {
return;
}
mStarted = true;
mLastTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mLooperRunnable);
mHandler.post(mLooperRunnable);
} @Override
public void stop() {
mStarted = false;
mHandler.removeCallbacks(mLooperRunnable);
}
}

对,没错我们看到了 mHandler.post(mLooperRunnable);金典的方法啊,那我们看看这mLooperRunnable方法里面在干嘛吧,

 long currentTime = SystemClock.uptimeMillis();
mSpringSystem.loop(currentTime - mLastTime);

废话不多说,立马去找我们想要看到的SpringSystem.loop方法了:

public void loop(double ellapsedMillis) {
for (SpringSystemListener listener : mListeners) {
listener.onBeforeIntegrate(this);
}
advance(ellapsedMillis);
if (mActiveSprings.isEmpty()) {
mIdle = true;
}
for (SpringSystemListener listener : mListeners) {
listener.onAfterIntegrate(this);
}
if (mIdle) {
mSpringLooper.stop();
}
}

对了,我们好像看到了advance(ellapsedMillis)啊,这个看起来比较吊,不说了进去看看再说啊:

 void advance(double deltaTime) {
for (Spring spring : mActiveSprings) {
// advance time in seconds
if (spring.systemShouldAdvance()) {
spring.advance(deltaTime / 1000.0);
} else {
mActiveSprings.remove(spring);
}
}
}

尼玛啊,我看到了

 spring.advance(deltaTime / 1000.0);

哈哈,我感觉快要找到了,不说,赶快进去找:

void advance(double realDeltaTime) {

    boolean isAtRest = isAtRest();

    if (isAtRest && mWasAtRest) {
/* begin debug
Log.d(TAG, "bailing out because we are at rest:" + getName());
end debug */
return;
} // clamp the amount of realTime to simulate to avoid stuttering in the UI. We should be able
// to catch up in a subsequent advance if necessary.
double adjustedDeltaTime = realDeltaTime;
if (realDeltaTime > MAX_DELTA_TIME_SEC) {
adjustedDeltaTime = MAX_DELTA_TIME_SEC;
} /* begin debug
long startTime = System.currentTimeMillis();
int iterations = 0;
end debug */ 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; // iterate over the true time
while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
/* begin debug
iterations++;
end debug */
mTimeAccumulator -= SOLVER_TIMESTEP_SEC; if (mTimeAccumulator < SOLVER_TIMESTEP_SEC) {
// This will be the last iteration. Remember the previous state in case we need to
// interpolate
mPreviousState.position = position;
mPreviousState.velocity = velocity;
} // Perform an RK4 integration to provide better detection of the acceleration curve via
// sampling of Euler integrations at 4 intervals feeding each derivative into the calculation
// of the next and taking a weighted sum of the 4 derivatives as the final output. // This math was inlined since it made for big performance improvements when advancing several
// springs in one pass of the BaseSpringSystem. // The initial derivative is based on the current velocity and the calculated acceleration
aVelocity = velocity;
aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity; // Calculate the next derivatives starting with the last derivative and integrating over the
// timestep
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; // Take the weighted sum of the 4 derivatives as the final output.
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; if (mTimeAccumulator > 0) {
interpolate(mTimeAccumulator / SOLVER_TIMESTEP_SEC);
} // End the spring immediately if it is overshooting and overshoot clamping is enabled.
// Also make sure that if the spring was considered within a resting threshold that it's now
// snapped to its end value.
if (isAtRest() || (mOvershootClampingEnabled && isOvershooting())) {
// Don't call setCurrentValue because that forces a call to onSpringUpdate
mStartValue = mEndValue;
mCurrentState.position = mEndValue;
setVelocity(0);
isAtRest = true;
} /* begin debug
long endTime = System.currentTimeMillis();
long elapsedMillis = endTime - startTime;
Log.d(TAG,
"iterations:" + iterations +
" iterationTime:" + elapsedMillis +
" position:" + mCurrentState.position +
" velocity:" + mCurrentState.velocity +
" realDeltaTime:" + realDeltaTime +
" adjustedDeltaTime:" + adjustedDeltaTime +
" isAtRest:" + isAtRest +
" wasAtRest:" + mWasAtRest);
end debug */ // NB: do these checks outside the loop so all listeners are properly notified of the state
// transition
boolean notifyActivate = false;
if (mWasAtRest) {
mWasAtRest = false;
notifyActivate = true;
}
boolean notifyAtRest = false;
if (isAtRest) {
mWasAtRest = true;
notifyAtRest = true;
}
for (SpringListener listener : mListeners) {
// starting to move
if (notifyActivate) {
listener.onSpringActivate(this);
} // updated
listener.onSpringUpdate(this); // coming to rest
if (notifyAtRest) {
listener.onSpringAtRest(this);
}
}
}
  • 1

代码精简一下,就变成这样了:

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; while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
/**
inner change
**/
} mTempState.position = tempPosition;
mTempState.velocity = tempVelocity; mCurrentState.position = position;
mCurrentState.velocity = velocity; for (SpringListener listener : mListeners) {
// starting to move
if (notifyActivate) {
listener.onSpringActivate(this);
} // updated
listener.onSpringUpdate(this); // coming to rest
if (notifyAtRest) {
listener.onSpringAtRest(this);
}
}
}
  • 1

这下看得十分清楚了吧,在while循环里面变换之后,我们得到了 :

  mCurrentState.position = position;
mCurrentState.velocity = velocity;

然后在接口回调中我们使用到了这些参数,

spring.addListener(new SpringListener() {

            @Override
public void onSpringUpdate(Spring spring) {
float value = (float) spring.getCurrentValue();
float scale = 1f - (value * mScale); ViewHelper.setScaleX(image, scale);
ViewHelper.setScaleY(image, scale);
} @Override
public void onSpringEndStateChange(Spring spring) {}
@Override
public void onSpringAtRest(Spring spring) {}
@Override
public void onSpringActivate(Spring spring) {}
});

就这样我么的变换终于就成功了,我们也就走通了相关的流程了,是不是很好玩呢???主要是facebook这个Rebound框架很小,类很少,我们可以在很短的时间对它通读,是不是感觉到分析源码很好玩呢?我也是一名android菜鸟,很多时候不敢去分析源码,很为很多的东西都看不懂看不透,那就慢慢来吧,先从简单的开始吧,呵呵。

最后,代码地址

Rebound动画框架简单介绍的更多相关文章

  1. jQuery系列 第一章 jQuery框架简单介绍

    第一章 jQuery框架简单介绍 1.1 jQuery简介 jQuery是一款优秀的javaScript库(框架),该框架凭借简洁的语法和跨平台的兼容性,极大的简化了开发人员对HTML文档,DOM,事 ...

  2. Django - Django框架 简单介绍

    Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码 ...

  3. Python -- Scrapy 框架简单介绍(Scrapy 安装及项目创建)

    Python -- Scrapy 框架简单介绍 最近在学习python 爬虫,先后了解学习urllib.urllib2.requests等,后来发现爬虫也有很多框架,而推荐学习最多就是Scrapy框架 ...

  4. 【FIORI系列】SAP OpenUI5 &lpar;SAPUI5&rpar; js框架简单介绍

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[FIORI系列]SAP OpenUI5 (SA ...

  5. Live555 实战之框架简单介绍

    作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 上一篇文章简要介绍了怎样以共享库的方式交叉编译Live555,今天再来介绍live源代码框架. 先 ...

  6. Spring框架简单介绍

    原文地址:  http://my.oschina.net/myriads/blog/37922 1.使用框架的意义与Spring的主要内容 随着软件结构的日益庞大,软件模块化趋势出现,软件开发也须要多 ...

  7. MoQ&lpar;基于&period;net3&period;5&comma;c&num;3&period;0的mock框架&rpar;简单介绍

    我们在做单元测试的时候,常常困扰于数据的持久化问题,很多情况下我们不希望单元测试影响到数据库中的内容,而且受数据库的影响有时我们的单元测试的速度会很慢,所以我们往往希望将持久化部分隔离开,做单元测试的 ...

  8. &period;NET 框架简单介绍

    初学.NET肯定会有一系列的疑问,比方(下面为自己的疑问): 1) 何为. NET框架.它都包括哪些东西? 2) 程序集是什么.它是怎样在CLR(通用语言执行时)中执行的? 3) C#与VB.NET同 ...

  9. Python&colon; Flask框架简单介绍

    接触Python之后我第一次听说Flask,我就根据自己搜罗的知识尽可能简洁的说出来.如果不准确的地方还请指正,谢谢. Flask是什么?             Flask是基于Python编写的微 ...

随机推荐

  1. python endswith和startwith

    转载:http://blog.sina.com.cn/s/blog_5dd2af0901012rmn.html 做文本处理的时候经常要判断一个文本有没有以一个子串开始,或者结束.Python为此提供了 ...

  2. GO语言练习:组合的用法

    1.代码 2.运行 1.代码 package main import "fmt" type Base struct { Name string } func (base * Bas ...

  3. unity,下面两个协程不等价

    //代码1 IEnumerator A(){ Debug.Log(“hi1”); { yield return new WaitForSeconds(1f); Debug.Log(“hi2”); } ...

  4. sql 2008 r2

    http://jingyan.baidu.com/article/6c67b1d6ca06f02787bb1ed1.html

  5. php设计模式之观察者模式

    观察者模式:能够更便利地创建查看目标对象状态的对象,并且提供与核心对象非耦合的指定功能性. 利用这种模式可以方便地创建一个对象(观察者),其可以用来“监视”另一个对象(被观察者)的状态.这样,就可以在 ...

  6. 爱你不容易 —— Stream详解

    作为前端,我们常常会和 Stream 有着频繁的接触.比如使用 gulp 对项目进行构建的时候,我们会使用 gulp.src 接口将匹配到的文件转为 stream(流)的形式,再通过 .pipe() ...

  7. 用CSS让DIV上下左右居中的方法

    转载自喜欢JS的无名小站 例如 一个父div(w:100%;h:400px)中有一个子div(w:100px;100px;).让其上下左右居中. 方法一(varticle-align) 理念 利用表格 ...

  8. 【BAT经典算法面试题系列】求和为n的连续正整数

    马上就要到9月份了,意味着一年一度的秋招就要开始了,相信不论是正在实习的童鞋还是马上就要找工作的童鞋,BAT无疑是国内的"明星企业",是每个学计算机的小伙伴们心之向往的企业,但是呢 ...

  9. vue中实现浏览器的复制功能

    点击复制,就可以实现copy <p class="inline-block"> <span >{{fenxiao.appSecret}}</span& ...

  10. 《Java并发编程实战》笔记-synchronized和ReentrantLock

    在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具.当震要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的.可轮询的与可中断的锁获取操作,公平队 ...