Rebound动画框架简单介绍

Rebound动画框架简单介绍

Android菜鸟一枚,有不对的地方希望大家指出,谢谢。

最近在接手了一个老项目,发现里面动画框架用的是facebook中的Rebound框架,由于以前没听说过,放假时闲得蛋痛,看看了源码,就顺手写这一篇吧。

写了一个小Demo,具体效果如下:

代码很简单,这是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菜鸟,很多时候不敢去分析源码,很为很多的东西都看不懂看不透,那就慢慢来吧,先从简单的开始吧,呵呵。

最后,代码地址

时间: 2024-10-12 17:16:03

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

Django - Django框架 简单介绍

Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码的Web应用框架, 由Python写成. 採用了MVC的软件设计模式, 即模型M, 视图V和控制器C. 它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的站点的, 并于2005年7月在BSD许可证下公布. 这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的.

Spring框架简单介绍

原文地址:  http://my.oschina.net/myriads/blog/37922 1.使用框架的意义与Spring的主要内容 随着软件结构的日益庞大,软件模块化趋势出现,软件开发也须要多人合作,随即分工出现.怎样划分模块,怎样定义接口方便分工成为软件project设计中越来越关注的问题.良好的模块化具有下面优势:可扩展.易验证.易维护.易分工.易理解.代码复用. 优良的模块设计往往遵守"低耦合高内聚"的原则.而"框架"是对开发中良好设计的总结,把设计中

.NET 框架简单介绍

初学.NET肯定会有一系列的疑问,比方(下面为自己的疑问): 1) 何为. NET框架.它都包括哪些东西? 2) 程序集是什么.它是怎样在CLR(通用语言执行时)中执行的? 3) C#与VB.NET同属于.NET平台,它们之间的根本联系和差别,为何他们之间的程序集能互相调用(假设创建一种新型的面向. NET的语言,要遵循什么)? 想要明确如上问题,就须要弄清楚CIL(通用中间语言).CLR(通用语言执行时).CTS(通用类型系统).CLS(通用语言规范)等等的概念,以下是自己看了一些他人的文章后

[转]缓存、缓存算法和缓存框架简单介绍

引言 我们都听过 cache,当你问他们是什么是缓存的时候,他们会给你一个完美的答案.可是他们不知道缓存是怎么构建的.或者没有告诉你应该採用什么标准去选择缓存框架. 在这边文章,我们会去讨论缓存.缓存算法.缓存框架以及哪个缓存框架会更好. 面试 "缓存就是存贮数据(使用频繁的数据)的暂时地方,由于取原始数据的代价太大了.所以我能够取得快一些." 这就是 programmer one (programmer one 是一个面试者)在面试中的回答(一个月前,他向公司提交了简历,想要应聘要求

H264 编解码框架简单介绍

阅读完H264/AVC 编解码器的介绍,脑海中仅仅是留下下面三条: 1.H264并没有明白规定一个编解码器怎样实现,仅仅是规定了一个编码后的视频比特流的句法,和该比特流的解码方法,这个与MPEG 类似. 2.H264和曾经的标准(如H261.H263.MPEG-1.MPEG-4)的编解码器实现流程没有太大差别,基本的不同在于各功能块的细节. 3.H264就是利用实现的复杂性获得压缩性能的明显改善.(至于复杂度的评估,以后会介绍) 以下介绍一下H264的编码器框图: 编码器採用的仍是变换和预測的混

Nodejs-express 4.0框架 简单介绍

http://www.expressjs.com.cn/4x/api.html  这个是express API的中文手册 但是只是翻译了一点 英语比较差比较难受. 1. 关于安装 现在网上查的时候有一部分 都是之前安装模式 就是导致 express 不是命令 问题 要使用 npm install express-generator -g 来安装 这个是官网的 网上还有其他方式 比较推荐官方的 2. 生成的目录结构 express -e ejs myapp 这种生成的就是ejs模板的 我就是使用这

client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

[本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 转载请加入该地址] 明天就是除夕了 预祝大家新春快乐 [ ]~( ̄▽ ̄)~* 天天饭局搞得我是身心疲惫= = 所以更新比較慢 今天想跟大家分享的就是这个大名鼎鼎的React框架 简单介绍 React是近两年非常流行的框架 流行到什么程度呢? 我看了一下Github上的数据 React达到了5w8+的star 在JavaScript中star排名第4 受欢迎程度可见一斑

js动画框架设计

题记: 当你不再依赖JQuery时,当你已经厌倦了引入js类库实现一些动画效果的方式,当你想实现一个简单而实用的动画框架......下面介绍下愚人设计的动画框架:支持动画缓动算法函数,如Linear.Cubic.Back.Bounce,支持改变高度,宽度,透明度,边框,外边距的基本动画,支持动画的回调函数,如开始.暂停.完成的callback等. Section One 游戏动画,Flash动画里一个比较重要的概念是帧频,即每秒播放多少帧动画,一般动画是30帧/秒,单位为fps(frames p

iOS开发——图形与动画篇OC篇&amp; POP简单介绍及使用

POP简单介绍及使用 前言 动画在APP开发过程中 大家多多少少都会接触到 而且随着ios7的扁平化风格启用之后 越来越多的APP开始尝试加入各种绚丽的动画交互效果以增加APP的用户体验(当然 还是以国外的APP居多) 有过相关开发经验的同学肯定知道在iOS中 动画相关的部分都是基于Core Animation 但是今天我们不讨论Core Animation 今天的主角是POP -来自于Facebook的动画引擎(其实我不喜欢把POP定义为动画引擎 我愿意称它为函数发生器) 介绍 官方地址 ht