一、前言

1.概述

  Google I/O'17推出了许多新的特性,在动画这一块又有新的API供开发者使用,在动画API中引入了DynamicAnimation,开发者可以使用新的API创建更加动态化的动画。

2.是什么

  Physics-based Animation,翻译过来就是基于物理的动画。在日常生活中当一个事物发生变化的时候,物理性的过渡或者说符合自然性的过渡,更容易让人们感知察觉,同样,更自然、不间断、有良好的发展趋势的动画会给我们带来更好的用户体验。Physics-based Animations是根据物理学的基本原理构建的动画,动画由力产生,当力趋于平衡时动画处于静止。

3.优点

  android提供的其他动画由固定的时间间隔和变化的动画值驱动。在运行过程中改变动画而不引入任何视觉破坏是非常具有挑战性的。
  使用Physics-based Animations api创建的动画可以追踪速度,在运动过程中动态地改变动画的目标值,正确规划路线,使动画看起来更加自然。
  自然的
  动画更灵活,模仿实时移动
  矫正方向
  动画在目标变化时保持动力,以更平稳的结束动画
  减少视觉冲击
  动画显示更加敏感和平滑,并减少整体的视觉中断。

二、使用

1.引入

  在app的build.gradle文件中加入:

compile 'com.android.support:support-dynamic-animation:26.0.0-beta2'

  在项目的build.gradle文件中加入:

allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
}
}

  注意:如果不加入这个,引入Physics-based Animation就会失败。

2.Fling Animation

FlingAnimation flingAnimation = new FlingAnimation(img, DynamicAnimation.X);
flingAnimation.setStartVelocity(500f);//设置初速度
flingAnimation.setFriction(0.5f);//设置摩擦力
flingAnimation.start();//开始动画

  FlingAnimationd的构造函数中img表示的是动画的view,DynamicAnimation.X表示动画的类型。
  使用setStartVelocity()方法赋予一个大于0的初速度,否则它不会动。
  Friction是摩擦力,在现实生活中如果一个物体保持一个速度在无摩擦力的情况下会一直运动下去。摩擦系数的值越大,说明摩擦力越大,动画越快停下来,默认值为1。
  调用start()方法开始动画。

3.Spring Animation

  Spring Animation弹性动画模仿弹簧的变化过程,基于施加在每个对象上的弹性力来计算值和速度。

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

  弹性动画支持以下View属性:
    ALPHA:表示视图中的Alpha透明度。默认值为1(不透明),值为0表示完全透明(不可见)。
    TRANSLATION属性:TRANSLATION_X,TRANSLATION_Y和TRANSLATION_Z。
    ROTATION、ROTATION_X和ROTATION_Y:这些属性控制旋转点在2D(rotation属性)和3D周围旋转。
    SCROLL_X和SCROLL_Y:这些属性指示源左侧和顶部边缘的滚动偏移(以像素为单位)。它也表示了页面滚动的位置。
    SCALE_X和SCALE_Y、X、Y和Z:这些用于描述视图在其容器中的最终位置的基本使用程序属性。
  和FlingAnimation一样,创建完SpringAnimation后我们需要设置初速度,接着创建了一个SpringForce实例,并设置了DampingRatio(弹性阻尼比)和Stiffness(刚性)。
  DampingRatio可以理解成反弹性次数,系统中有以下几个可选:
    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。
  阻尼比描述弹簧振荡的阻力大小:
    当阻尼比大于1时发生过阻,它使对象快速返回到休息位置。
    当阻尼比等于1时发生临界阻尼。它使对象在最短的时间内返回到休息位置。
    当阻尼比小于1时发生欠阻尼。它通过传递静止位置使物体多次过冲,然后逐渐达到静止位置。
    当阻尼比等于零时,就会发生阻尼。它让对象永远振荡起来。
  Stiffness可以理解成要恢复成未拉伸状态所需的时间,系统中有以下几个可选:
    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,数值越大,恢复到之前状态的时间就越短。
  刚性定义弹簧常数,用于测量弹簧的强度。当弹簧不在静止位置时,刚性弹簧对附着的物体施加更大的力。
  setFinalPosition()方法指定最后静止时的位置。

4.创建自定义的动画属性

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

FloatPropertyCompat<View> scale = new FloatPropertyCompat<View>("scale") {
@Override
public float getValue(View object) {
//返回当前属性值
return object.getScaleX();
} @Override
public void setValue(View object, float value) {
//更新要修改的动画属性
object.setScaleX(value);
object.setScaleX(value);
}
}; SpringAnimation stretchAnimation = new SpringAnimation(img, scale);
stretchAnimation.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);//传递一个有意义的值,以确保动画不会消耗太多的CPU性能
SpringForce force = new SpringForce(1);
force.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY)
.setStiffness(SpringForce.STIFFNESS_VERY_LOW); stretchAnimation.setSpring(force).setStartVelocity(100);
stretchAnimation.start();

  创建FloatPropertyCompat实例,在setValue()方法中更新要修改的动画属性,在getValue()方法中返回当前属性值,示例代码统一改变了SCALE_X和SCALE_Y属性,自定义属性创建好之后可以像其他动画属性一样使用它。
  在创建使用自定义属性的动画时,最好也调用setMinimumVisibleChange()方法并传递一个有意义的值,以确保动画不会消耗太多的CPU性能。

5.动画监听

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

  当动画结束时变换表情。

SpringAnimation springAnimation = new SpringAnimation(img, DynamicAnimation.X);
SpringForce springForce = new SpringForce();
springForce.setFinalPosition(img.getX());
springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
springForce.setStiffness(SpringForce.STIFFNESS_LOW); springAnimation.setSpring(springForce);
springAnimation.setStartVelocity(2000);
springAnimation.start(); img.setImageResource(R.drawable.ic_sentiment_very_satisfied_black_56dp);
springAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
@Override
public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity) {
img.setImageResource(R.drawable.ic_sentiment_neutral_black_56dp);
}
});

6.一个使用动画的例子

  使用三个圆圈,当拖动上面第一个圆圈的时候,下面两个圆圈跟着移动。

public class ChainedSpringActivity extends Activity {

    private float mDampingRatio = 1.0f;
private float mStiffness = 50.0f; private View lead;
private View follow1;
private View follow2;
private SeekBar dr;
private SeekBar stiff;
private TextView drTxt;
private TextView nfTxt; private SpringAnimation animate1X;
private SpringAnimation animate1Y;
private SpringAnimation animate2X;
private SpringAnimation animate2Y; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chained_spring);
lead = findViewById(R.id.lead);
follow1 = findViewById(R.id.follow1);
follow2 = findViewById(R.id.follow2);
dr = findViewById(R.id.damping_ratio);
stiff = findViewById(R.id.stiffness);
drTxt = findViewById(R.id.damping_ratio_txt);
nfTxt = findViewById(R.id.stiffness_txt); animate1X = new SpringAnimation(follow1, DynamicAnimation.TRANSLATION_X, lead.getTranslationX());//DynamicAnimation.TRANSLATION_X表示动画的模式, lead.getTranslationX()表示动画停止的位置
animate1Y = new SpringAnimation(follow1, DynamicAnimation.TRANSLATION_Y, lead.getTranslationY());
animate2X = new SpringAnimation(follow2, DynamicAnimation.TRANSLATION_X, follow2.getTranslationX());
animate2Y = new SpringAnimation(follow2, DynamicAnimation.TRANSLATION_Y, follow2.getTranslationY()); animate1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
animate2X.animateToFinalPosition(value);//指定最后静止时的位置
}
}); animate1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
animate2Y.animateToFinalPosition(value);//指定最后静止时的位置
}
}); ((View) lead.getParent()).setOnTouchListener(new View.OnTouchListener() {
private float firstDownX = 0;
private float firstDownY = 0; @Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {//如果触摸的范围不在lead按钮上,则返回false
if (motionEvent.getX() < lead.getX()
|| motionEvent.getX() > lead.getX() + lead.getWidth()
|| motionEvent.getY() < lead.getY()
|| motionEvent.getY() > lead.getY() + lead.getHeight()
) {
return false;
} //设置动画的设置弹性阻尼与生硬度
animate1X.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness);
animate1Y.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness);
animate2X.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness);
animate2Y.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness); firstDownX = motionEvent.getX() - lead.getTranslationX();
firstDownY = motionEvent.getY() - lead.getTranslationY();
} else if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
float deltaX = motionEvent.getX() - firstDownX;
float deltaY = motionEvent.getY() - firstDownY; //移动lead的位置
lead.setTranslationX(deltaX);
lead.setTranslationY(deltaY); //设置动画最后的位置
animate1X.animateToFinalPosition(deltaX);
animate1Y.animateToFinalPosition(deltaY);
}
return true;
}
});
setupSeekBars();
} private void setupSeekBars() {
dr.setMax(130);
dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (i < 80) {
mDampingRatio = i / 80.0f;
} else if (i > 90) {
mDampingRatio = (float) Math.exp((i - 90) / 10.0);
} else {
mDampingRatio = 1;
}
drTxt.setText(String.format("%.4f", (float) mDampingRatio));
} @Override
public void onStartTrackingTouch(SeekBar seekBar) { } @Override
public void onStopTrackingTouch(SeekBar seekBar) { }
}); stiff.setMax(110);
stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
float stiffness = (float) Math.exp(i / 10d);
mStiffness = stiffness;
nfTxt.setText(String.format("%.3f", (float) stiffness));
} @Override
public void onStartTrackingTouch(SeekBar seekBar) { } @Override
public void onStopTrackingTouch(SeekBar seekBar) { }
}); dr.setProgress(80);
stiff.setProgress(60);
}
}

详细代码地址:https://github.com/ZhangMiao147/PhysicsBasedAnimationDemo

参考文章

http://rkhcy.github.io/2017/07/07/PhysicsBasedAnimation%E5%AD%A6%E4%B9%A0/
    http://blog.csdn.net/lyric_315/article/details/72758650

PhysicsBasedAnimation学习记录的更多相关文章

  1. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  2. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  3. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  4. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  5. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  6. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

  7. UWP学习记录9-设计和UI之控件和模式6

    UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...

  8. UWP学习记录8-设计和UI之控件和模式5

    UWP学习记录8-设计和UI之控件和模式5 1.日历.日期和时间控件 日期和时间控件提供了标准的本地化方法,可供用户在应用中查看并设置日期和时间值. 有四个日期和时间控件可供选择,选择的依据如下: 日 ...

  9. UWP学习记录7-设计和UI之控件和模式4

    UWP学习记录7-设计和UI之控件和模式4 1.翻转视图 使用翻转视图浏览集合中的图像或其他项目(例如相册中的照片或产品详细信息页中的项目),一次显示一个项目. 对于触摸设备,轻扫某个项将在整个集合中 ...

随机推荐

  1. 01 json环境搭建

    1 导包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.o ...

  2. matlab错误:Subscript indices must either be real positive integers or logicals.

    matlab错误:Subscript indices must either be real positive integers or logicals. 中文解释:下标索引必须是正整数类型或者逻辑类 ...

  3. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  4. 2.Node.js access_token的获取、存储及更新

    文章目录:         1.Node.js 接入微信公众平台开发         2.Node.js access_token的获取.存储及更新 一.写在前面的话   上一篇文章中,我们使用 No ...

  5. SQL数据库的多表查询

    多表查询分为 内.外连接 外连接分为左连接(left join 或left outer join).右连接(right join 或者 right outer join).和完整外部连接 (full ...

  6. Bash函数

    一.什么是Bash函数 Bash不支持goto语句,可以用function实现程序流程跳转.当前shell中一组组织在一起并被命名的命令.比脚本的效率高,一旦定义,就成为shell内存的一部分,可以随 ...

  7. 开始学习机器学习,从Ng的视频开始

    时隔开5个月,忙完了考研和毕设后终于有时间搞自己想搞得,研究生导师方向是图像处理与机器学习结合,重新开工 何为机器学习? 对于机器学习(Machine Learning)的定义大体上有两种,第一种是美 ...

  8. XCOM2中敌对生物设计分析(Aliens篇)

    Aliens Aliens作为游戏设定中入侵的外星人,有各式外貌及奇特的战斗方式,掌握一些高能科技或利用精神力量进行攻击 Sectoid 使用灵能战斗的外星人,并无高级版本,初级便会使用精神控制,生命 ...

  9. web移动端布局方式整理

    写H5页面一直写的有点随意,只是保证了页面在各个屏幕下显示良好,却没有保证到在各个屏幕下是等比例放大或者缩小.这些天在写一些页面,试着看看能不能写出等比例放大缩小的页面,发现不容易啊,在网上找了一些文 ...

  10. maven 不同环境加载不同的properties 文件

    http://haohaoxuexi.iteye.com/blog/1900568 //参考文章 实际项目中pom配置如下 <profiles> <profile> <i ...