ViewPropertyAnimator
使用方式:View.animate() 后跟 translationX() 等方法,动画会自动执行
。
view.animate().translationX(500);
ObjectAnimator
使用方式:
- 如果是自定义控件,需要添加 setter / getter 方法,并在
setter
方法的最后调用invalidate()
方法,刷新绘制; - 用 ObjectAnimator.ofXXX() 创建 ObjectAnimator 对象;
- 用 start() 方法执行动画。
public class SportsView extends View {
float progress = 0;
......
// 创建 getter 方法
public float getProgress() {
return progress;
}
// 创建 setter 方法
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
......
canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);
......
}
}
......
// 创建 ObjectAnimator 对象
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
// 执行动画
animator.start();
设置监听器
给动画设置监听器,可以在关键时刻得到反馈,从而及时做出合适的操作,例如在动画的属性更新时同步更新其他数据,或者在动画结束后回收资源等。
设置监听器的方法, ViewPropertyAnimator
和 ObjectAnimator
略微不一样: ViewPropertyAnimator
用的是 setListener()
和 setUpdateListener()
方法,可以设置一个监听器
,要移除监听器时通过 set[Update]Listener(null) 填 null 值来移除;而 ObjectAnimator
则是用 addListener()
和 addUpdateListener()
来添加一个或多个监听器
,移除监听器则是通过 remove[Update]Listener() 来指定移除对象。
另外,由于 ObjectAnimator
支持使用 pause()
方法暂停,所以它还多了一个 addPauseListener() / removePauseListener()
的支持; 而 ViewPropertyAnimator
则独有 withStartAction()
和 withEndAction()
方法,可以设置一次性的动画开始或结束的监听,在动画执行结束后就自动丢弃,就算之后再重用 ViewPropertyAnimator
来做别的动画,用它们设置的回调也不会再被调用。而 set/addListener() 所设置的 AnimatorListener 是持续有效的,当动画重复执行时,回调总会被调用。
需要说明一下的是,就算动画被取消,onAnimationEnd()
也会被调用。所以当动画被取消时,如果设置了 AnimatorListener
,那么 onAnimationCancel()
和 onAnimationEnd()
都会被调用。onAnimationCancel()
会先于 onAnimationEnd()
被调用。
withEndAction()
设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和 AnimatorListener.onAnimationEnd()
的行为是不一致的。
TypeEvaluator
关于 ObjectAnimator
,上面讲到可以用 ofInt()
来做整数的属性动画和用ofFloat()
来做小数的属性动画。这两种属性类型是属性动画最常用的两种,不过在实际的开发中,可以做属相动画的类型还是有其他的一些类型。当需要对其他类型来做属性动画的时候,就需要用到 TypeEvaluator
了。
自定义 Evaluator
借助于 TypeEvaluator
,属性动画就可以通过 ofObject()
来对不限定类型的属性做动画了。方式很简单:
private class PointFEvaluator implements TypeEvaluator<PointF> {
PointF newPoint = new PointF();
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
float x = startValue.x + (fraction * (endValue.x - startValue.x));
float y = startValue.y + (fraction * (endValue.y - startValue.y));
newPoint.set(x, y);
return newPoint;
}
}
ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();
PropertyValuesHolder 同一个动画中改变多个属性
很多时候,你在同一个动画中会需要改变多个属性,例如在改变透明度的同时改变尺寸。如果使用 ViewPropertyAnimator,你可以直接用连写的方式来在一个动画中同时改变多个属性:
view.animate()
.scaleX(1)
.scaleY(1)
.alpha(1);
ObjectAnimator 同一个动画中改变多个属性
使用 PropertyValuesHolder
来同时在一个ObjectAnimator
动画中改变多个属性。
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
AnimatorSet 多个动画配合执行
有的时候,你不止需要在一个动画中改变多个属性,还会需要多个动画配合工作,比如,在内容的大小从 0 放大到 100% 大小后开始移动。这种情况使用 PropertyValuesHolder
是不行的,因为这些属性如果放在同一个动画中,需要共享动画的开始时间、结束时间、Interpolator 等等一系列的设定,这样就不能有先后次序地执行动画了。
这就需要用到 AnimatorSet 了。
ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
// 两个动画依次执行
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();
// 两个动画同时执行
animatorSet.playTogether(animator1, animator2);
animatorSet.start();
// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式来精确配置各个 Animator 之间的关系
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
PropertyValuesHolders.ofKeyframe() 把同一个属性拆分
// 在 0% 处开始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
// 时间经过 50% 的时候,动画完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
// 时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
animator.start();
ValueAnimator 最基本的轮子
额外简单说一下 ValuesAnimator
。很多时候,你用不到它,只是在你使用一些第三方库的控件,而你想要做动画的属性却没有 setter / getter
方法的时候,会需要用到它。
ValueAnimator 并不常用,因为它的功能太基础了。ValueAnimator 是 ObjectAnimator 的父类,实际上,ValueAnimator 就是一个不能指定目标对象版本的 ObjectAnimator。
ObjectAnimator 是自动调用目标对象的 setter 方法来更新目标属性的值,以及很多的时候还会以此来改变目标对象的 UI,而 ValueAnimator 只是通过渐变的方式来改变一个独立的数据,这个数据不是属于某个对象的,至于在数据更新后要做什么事,全都由你来定,你可以依然是去调用某个对象的 setter 方法(别这么为难自己),也可以做其他的事,不管要做什么,都是要你自己来写的,ValueAnimator 不会帮你做。
比如有的时候,你要给一个第三方控件做动画,你需要更新的那个属性没有 setter 方法,只能直接修改,这样的话 ObjectAnimator 就不灵了啊。怎么办?这个时候你就可以用 ValueAnimator
,在它的 onUpdate()
里面更新这个属性的值,并且手动调用 invalidate()
。
所以,ViewPropertyAnimator、ObjectAnimator、ValueAnimator 这三种 Animator,它们其实是一种递进的关系:从左到右依次变得更加难用,也更加灵活。但我要说明一下,它们的性能是一样的,因为 ViewPropertyAnimator 和 ObjectAnimator 的内部实现其实都是 ValueAnimator,ObjectAnimator 更是本来就是 ValueAnimator 的子类,它们三个的性能并没有差别。它们的差别只是使用的便捷性以及功能的灵活性。所以在实际使用时候的选择,只要遵循一个原则就行:尽量用简单的。
能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。
目录结构
- Android 自定义View基础(一)
- Android自定义View:View(二)
- Android自定义View:ViewGroup(三)
- Android 自定义View:处理事件分发(四)
- Android 自定义View:深入理解自定义属性(七)