PhysicsBasedAnimation学习

概述

Google I/O ‘17推出了许多新的特性,在动画这一块又有新的API供开发者使用,具体视频请见Android Animations Spring to Life (Google I/O ‘17),主要介绍了Physics-based Animations,在动画API中引入了DynamicAnimation,并介绍了它的两个子类FlingAnimation和SpringAnimation的使用,开发者可以使用新的API创建更加动态化的动画。

Spring Animation

Spring Animation

FlingAnimation

FlingAnimation

是什么

Physics-based Animations,翻译过来就是基于物理的动画,官网上有很详细的介绍,在日常生活中当一个事物发生变化的时候,物理性的过渡或者说符合自然性的过渡,更容易让我们感知察觉,同样,更自然、不间断、有良好发展趋势的动画会给我们带来更好的用户体验。Physics-based Animations是根据物理学的基本原理构建的动画,动画由力产生,当力趋于平衡时动画处于静止。让我们重新捡起高中半吊子水平的物理知识,比如给物体在某个方向上施加一个力,物体有了速度,会在该方向上运动,如果停止施力,最后物体会由于摩擦力的影响,速度逐渐减小,运动一段时间后处于静止状态。Physics-based Animations概括起来就是下面几点:

  • 动画由力驱动
  • 力决定了动画的加速和减速
  • 在每一帧中动画值和速度都会更新
  • 当受力达到平衡时动画停止

好处

使用Physics-based Animations api创建的动画可以追踪速度,在运动过程中动态地改变动画的目标值,正确规划路线,使动画看起来更加自然。看下两组动画

对比了两组动画的差别,图1动画无法追踪速度,在进行下一帧的时候它的速度几乎还是从0开始的,速度值突然的变化给用户不连贯的视觉体验。图2动画可以追踪速度,在第二阶段力的方向改变,导致原先的速度发生变化,图片看起来很自然地移动到新的位置

动画1的速度曲线图:

动画2的速度曲线图:

怎么用

  1. Android Studio 3.0 Canary 4

  2. 在Android Studio的build.gradle中添加依赖

1
2
3
4
dependencies {
...
implementation 'com.android.support:support-dynamic-animation:26.0.0-beta2'
}

Fling Animation

看下怎样创建FlingAnimation:

1
2
3
4
5
ImageView img = root.findViewById(R.id.img_simple_fling);
FlingAnimation flingAnimation = new FlingAnimation(img, DynamicAnimation.X);
flingAnimation.setStartVelocity(500f);
flingAnimation.setFriction(0.5f);
flingAnimation.start();

效果如下:

解释下上面的代码:
创建一个FlingAnimation实例,默认情况下该实例的初速度是0pixels/s,因此我们需要调用setStartVelocity()方法给它赋予一个大于0的初速度,否则它是不会动的;另外这里介绍下Friction,翻译过来就是摩擦力的意思,在现实生活中如果一个物体保持一个速度在无摩擦力的情况下会一直运动下去,这里也是(比如这里设置Fraction为0.01f,发现小球滚到屏幕外了),我们需要给该实例设置一个摩擦系数,设置的值越大,说明摩擦力越大,动画越快停下来,默认该值为1;最后调用start()方法开始动画。

Spring Animation

看下怎样创建SpringAnimation:

1
2
3
4
5
6
7
8
9
ImageView img = root.findViewById(R.id.img_simple_spring);
SpringAnimation springAnimation = new SpringAnimation(img, DynamicAnimation.X);
springAnimation.setStartVelocity(2000);
SpringForce springForce = new SpringForce();
springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
springForce.setStiffness(SpringForce.STIFFNESS_LOW);
springForce.setFinalPosition(img.getX());
springAnimation.setSpring(springForce);
springAnimation.start();

效果如下:

解释下上面的代码:

和FlingAnimation一样,创建完SpringAnimation后我们需要设置初速度,接着创建了一个
SpringForce实例,并设置了DampingRatio(弹性阻尼)和Stiffness(生硬度),
DampingRatio可以理解成反弹次数,系统中有以下几个可选

1
2
3
4
public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2F;
public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5F;
public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75F;
public static final float DAMPING_RATIO_NO_BOUNCY = 1.0F;

默认设置为DAMPING_RATIO_MEDIUM_BOUNCY,在官网上贴了四张很Q弹的图片,分别对应不同值的效果,该值越大,反弹次数越少,值为1时不反弹。

  • DAMPING_RATIO_HIGH_BOUNCY
  • DAMPING_RATIO_MEDIUM_BOUNCY
  • DAMPING_RATIO_LOW_BOUNCY
  • DAMPING_RATIO_NO_BOUNCY

Stiffness可以理解成要恢复成未拉伸状态所需的时间,系统中有以下几个可选

1
2
3
4
public static final float STIFFNESS_HIGH = 10000.0F;
public static final float STIFFNESS_MEDIUM = 1500.0F;
public static final float STIFFNESS_LOW = 200.0F;
public static final float STIFFNESS_VERY_LOW = 50.0F;

默认设置为STIFFNESS_MEDIUM,在官网同样贴了四张对应不同值得对比图,该值越大,恢复到之前状态的时间就越短。可以修改DampingRatio或Stiffness查看效果

  • STIFFNESS_HIGH
  • STIFFNESS_MEDIUM
  • STIFFNESS_LOW
  • STIFFNESS_VERY_LOW

setFinalPosition()方法指定最后静止时的位置。

创建自定义的动画属性

SpringAniamtion和FlingAnimation的构造函数只能接收一个可动画属性参数,如ALPHA、ROTATION、SCALE等,如果要同时为多个属性生成动画,一种方法是创建多个对应类的实例,然后传入要改变的动画值,这种做法比较麻烦,我们可以创建一个新的属性,该属性封装了我们想改变的其他动画属性值,做法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FloatPropertyCompat<View> scale = 
new FloatPropertyCompat<View>("scale") {
@Override
public float getValue(View view) {
// return the value of any one property
return view.getScaleX();
}

@Override
public void setValue(View view, float value) {
// Apply the same value to two properties
view.setScaleX(value);
view.setScaleY(value);
}
};

创建FloatPropertyCompat实例,在setValue()方法中更新要修改的动画属性,在getValue()方法中返回当前属性值,示例代码统一改变了SCALE_X和SCALE_Y属性,自定义属性创建好之后可以像其他动画属性一样使用它,

1
2
SpringAnimation stretchAnimation =
new SpringAnimation(emoji, scale);

在创建使用自定义属性的动画时,最好也调用setMinimumVisibleChange()方法并传递一个有意义的值,以确保动画不会消耗太多的CPU性能

1
2
stretchAnimation.setMinimumVisibleChange(
DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);

效果如下:

动画监听

DynamicAnimation提供了两个动画监听器OnAnimationUpdateListener和 OnAnimationEndListener,从名字也可以猜到前者监听动画值改变,后者监听动画结束状态。添加动画变化监听需要调用addUpdateListener()方法,重写onAnimationUpdate()方法执行具体操作;添加动画结束监听需要调用addEndListener()方法,重写onAnimationEnd()方法执行具体操作;若要移除动画监听,则需要调用removeUpdateListener()和removeEndListener()。监听动画结束的使用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Change icon before animation starts
emoji.setImageResource(
R.drawable.ic_sentiment_very_satisfied_black_56dp);

// Start animation
springAnimation.start();

springAnimation.addEndListener(
new DynamicAnimation.OnAnimationEndListener() {
@Override
public void onAnimationEnd(DynamicAnimation animation,
boolean canceled,
float value, float velocity) {
// Change icon after animation ends
emoji.setImageResource(
R.drawable.ic_sentiment_neutral_black_56dp);
}
});

当动画结束时变换表情,效果如下:

监听动画变化的使用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Creating two views to demonstrate the registration of the update listener.
final View view1 = findViewById(R.id.view1);
final View view2 = findViewById(R.id.view2);
// Setting up a spring animation to animate the view1 and view2 translationX and translationY properties
final SpringAnimation anim1X = new SpringAnimation(view1,
DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim1Y = new SpringAnimation(view1,
DynamicAnimation.TRANSLATION_Y);
final SpringAnimation anim2X = new SpringAnimation(view2,
DynamicAnimation.TRANSLATION_X);
final SpringAnimation anim2Y = new SpringAnimation(view2,
DynamicAnimation.TRANSLATION_Y);
// Registering the update listener
anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
// Overriding the method to notify view2 about the change in the view1’s property.
@Override
public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
float velocity) {
anim2X.animateToFinalPosition(value);
}
});
anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
@Override
public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
float velocity) {
anim2Y.animateToFinalPosition(value);
}
});

我们可以结合animateToFinalPosition()方法实现链式弹力动画效果,即一个View的动画依赖于另一个,效果如下:

Thanks to

GitHub Demo传送门

评论

Ajax Android AndroidStudio Animation Anroid Studio AppBarLayout Banner Buffer Bulma ByteBuffer C++ C11 C89 C99 CDN CMYK COM1 COM2 CSS Camera Raw, 直方图 Chrome ContentProvider CoordinatorLayout C语言 DML DOM Dagger Dagger2 Darktable Demo Document DownloadManage Element Error Exception Extensions File FileProvider Fresco GCC Git GitHub GitLab Gradle Groovy HTML5 Handler HandlerThread Hexo Hybrid I/O IDEA IO ImageMagick IntelliJ Intellij Interpolator JCenter JNI JS Java JavaScript JsBridge Kotlin Lab Lambda Lifecycle Lint Linux Looper MQTT MVC MVP Maven MessageQueue Modbus Momentum MySQL NDK NIO NexT Next Nodejs ObjectAnimator Oracle VM Permission PhotoShop Physics Python RGB RS-232 RTU Remote-SSH Retrofit Runnable RxAndroid RxJava SE0 SSH Spring SpringBoot Statubar Task Theme Thread Tkinter UI UIKit UML VM virtualBox VS Code ValueAnimator ViewPropertyAnimator Web Web前端 Workbench api apk bookmark by关键字 compileOnly css c语言 databases demo hexo hotfix html iOS icarus implementation init jQuery javascript launchModel logo merge mvp offset photos pug query rxjava2 scss servlet shell svg tkinter tomcat transition unicode utf-8 vector virtual box vscode 七牛 下载 中介者模式 串口 临潼石榴 主题 书签 事件 享元模式 仓库 代理模式 位运算 依赖注入 修改,tables 光和色 内存 内核 内部分享 函数 函数式编程 分支 分析 创建 删除 动画 单例模式 压缩图片 发布 可空性 合并 同向性 后期 启动模式 命令 命令模式 响应式 响应式编程 图层 图床 图片压缩 图片处理 图片轮播 地球 域名 基础 增加 备忘录模式 外观模式 多线程 大爆炸 天气APP 太白山 头文件 奇点 字符串 字符集 存储引擎 宇宙 宏定义 实践 属性 属性动画 岐山擀面皮 岐山肉臊子 岐山香醋 工具 工厂模式 年终总结 开发技巧 异常 弱引用 恒星 打包 技巧 指针 插件 摄影 操作系统 攻略 故事 数据库 数据类型 数组 文件 新功能 旅行 旋转木马 时序图 时空 时间简史 曲线 杂谈 权限 枚举 架构 查询 标准库 标签选择器 样式 核心 框架 案例 桥接模式 检测工具 模块化 模板引擎 模板方法模式 油泼辣子 泛型 洛川苹果 浅色状态栏 源码 瀑布流 热修复 版本 版本控制 状态栏 状态模式 生活 留言板 相册 相对论 眉县猕猴桃 知识点 码云 磁盘 科学 笔记 策略模式 类图 系统,发行版, GNU 索引 组件 组合模式 结构 结构体 编码 网易云信 网格布局 网站广播 网站通知 网络 美化 联合 膨胀的宇宙 自定义 自定义View 自定义插件 蒙版 虚拟 虚拟机 补码 补齐 表单 表达式 装饰模式 西安 观察者模式 规范 视图 视频 解耦器模式 设计 设计原则 设计模式 访问者模式 语法 责任链模式 贪吃蛇 转换 软件工程 软引用 运算符 迭代子模式 适配器模式 选择器 通信 通道 配置 链表 锐化 错误 键盘 闭包 降噪 陕西地方特产 面向对象 项目优化 项目构建 黑洞
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×