Android动画(五):插值器和估值器补充、Fling动画、物理动画

Catalogue
  1. 1 Path类型的插值器
  2. 2 估值器实现路径动画
  3. 3 Fling动画
  4. 4 Spring动画
  5. 参考资料

笔者已将本节的代码上传至 Github,大家可以结合着学习。
本篇讲的是 Android 视图位置变化相关的知识,其中,第一部分是对之前文章视图动画、属性动画中插值器、估值器的补充。本章的内容主要参考了官方的下面三篇文章:
1、Move a View with Animation
2、Move views using a fling animation
3、Animate movement using spring physics
参考的中文文章在文末有给出。

1 Path类型的插值器

Path 类型的插值器是一种新的插值器,Android 从5.0开始支持。可以用代码、XML 两种方式实现,这里我们只讨论代码实现的方式:

1
2
3
4
5
6
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Path path = new Path();
path.lineTo(1f, 1f);
PathInterpolator pathInterpolator = new PathInterpolator(path);
…………
}

从以上代码可以看出,Path 被限制在一个1*1的正方形中,x、y坐标的取值范围都为[0,1],并且 Path 必须从(0,0)坐标开始,(1,1)坐标结束。x坐标代表时间进度:0-100%,y坐标代表当前时间点对应的动画完成度:0-100%。
一旦pathInterpolator创建完成,就可以通过Animator.setInterpolator()方法使用它了。

1
2
3
ObjectAnimator animation = ObjectAnimator.ofFloat(view, "translationX", 100f);
animation.setInterpolator(pathInterpolator);
animation.start();

2 估值器实现路径动画

ObjectAnimator从 Android 5.0 开始提供了一类新的构造方法用于实现沿自定义路径移动的动画。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Path path = new Path();
path.moveTo(view.getX(), view.getY());
path.cubicTo(view.getX(), view.getY(),
view.getX() + 300, view.getY() + 200,
view.getX() - 400, view.getY() + 500);
//下面两种方法不允许
//path.addCircle(view.getX(), view.getY(),100,Path.Direction.CCW);
//path.arcTo(view.getX()+100, view.getY()+100, view.getX() + 500, view.getY() + 500, 270f, -180f, true);
ObjectAnimator objectAnimator =
ObjectAnimator.ofFloat(ivBalPathAnimator, View.X, View.Y, path);
objectAnimator.setDuration(1200);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
}

3 Fling动画

Fling 动画使用物理学中摩擦力的概念,使得 view 的速度逐渐被降低,从而实现了现实中的阻力运动。Android 4.4版本开始支持该动画。

** 1、添加支持库 **

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

** 2、创建fling动画 **
FlingAnimation 类可以帮助你创建fling动画,只要在创建 FlingAnimation 实例时传入操作的 View 对象以及操作对象的属性即可。

1
FlingAnimation fling = new FlingAnimation(view, DynamicAnimation.SCROLL_X);

** 3、设置初始速度 **
为了让物体移动(并非只有移动),我们必须给物体一个初速度。默认的初速度是0,所以我们必须定义一个大于0的初速度以保证物体能够移动。
设置初速度的方法是setStartVelocity()。您可以使用固定值作为起始速度,也可以将其基于触摸手势的速度通过GestureDetetor.OnGestureListener 获取。如果您选择提供固定值,您应该以每秒dp的值定义值,然后将其转换为每秒像素数。以每秒dp定义值的方式允许速度独立于设备的密度和形状因子。有关将起始速度转换为每秒像素数的更多信息,将在下一篇文章 《Spring Animation》中的“ 每秒转换为每秒像素数”部分讲解 。

** 4、设置动画的取值范围 **
你可以通过setMinValue()setMaxValue()设置属性值的变化范围,这个对于有明确变化范围的属性显得十分重要,例如透明度(0-1)。注意,当fling时的属性值到达设定的最大值或者最小值时,动画会立刻结束。

** 5、设置阻力因子 **
你可以通过setFriction()改变动画的阻力因子,他定义了速度下降的速度。阻力因子的默认值是1。
下面的例子定义了一个横向滑动的fling动画,滑动边界被设置为0和maxScroll,摩擦力设置为1.1。

1
2
3
4
5
6
FlingAnimation fling = new FlingAnimation(view, DynamicAnimation.SCROLL_X);
fling.setStartVelocity(-velocityX)
.setMinValue(0)
.setMaxValue(maxScroll)
.setFriction(1.1f)
.start();

注意,上面的方法仅仅适用于单个属性的 fling 变化。如果有多个属性同时需要fling变化呢?有的文章倒是建议用FloatPropertyCompat实现。可是FloatPropertyCompat有它的局限性,正如这篇文章的第五部分所说:要在确保所有属性的值不是完全相互独立的情况下,该方法才适用。否则,我们只能针对每个属性都建立一个 FlingAnimation。

** 6、设置最小可见变化 **
当您在为不以像素为单位定义的自定义属性设置动画时,应设置用户可见的动画值的最小更改,它作为定义动画结束的最小值。
当操作 DynamicAnimation.ViewProperty 动画时不需要调用此方法,因为此时的最小可见变化可以自动获取。例如:
1、默认最小可见的变化值是1个像素,如:TRANSLATION_X,TRANSLATION_Y, TRANSLATION_Z,SCROLL_X,和 SCROLL_Y。
2、对于使用旋转动画,如ROTATION, ROTATION_X,和ROTATION_Y,最小可见变化是 MIN_VISIBLE_CHANGE_ROTATION_DEGREES,或1/10像素。
3、对于使用不透明度的动画,最小可见变化为 MIN_VISIBLE_CHANGE_ALPHA 或者1/256。
要设置动画的最小可见变化,请调用setMinimumVisibleChange()方法,并传递最小可见常量或您需要为自定义属性计算的值。有关计算此值的更多信息,请参阅本部分末的“计算最小可见变化值”部分。

1
anim.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);

注意:只有在为不以像素定义的自定义属性制作动画时,才需要设置最小可见变化值。

计算最小可见变化值 :
最小可见变化值=自定义属性值的范围/以像素为单位的动画执行范围。
例如,要进行动画制作的自定义非像素为单位的属性变化范围是从0到100,同时如果此过程对应于200像素的动画更改。根据公式,最小可见变化值是100/200等于0.5像素。

4 Spring动画

这一部分的内容在官网讲得也是很明白了。需要注意的是,官网结尾最后的“Cancel animation”部分提到的‘cancel()’和‘skipToEnd()’使用注意事项:

cancel():立即停止动画
skipToEnd():恢复到最终位置并停止动画。需要注意的是,在无阻尼的情况下,不能调用该方法(即:DampingRatio==0时)。为了安全,可以先调用 canSkipToEnd() 进行判断,有阻尼的情况下返回 true,否则返回 false。一般来说,skipToEnd() 会有跳跃的效果。

以下是Demo中实现的效果:


参考资料

1、中文翻译:使用 fling 动画移动视图
2、SpringAnimation 详解