1、简介
ViewPropertyAnimator其实算不上什么高级技巧,它的用法格外的简单,只不过和前面所学的所有属性动画的知识不同,它并不是在3.0系统当中引入的,而是在3.1系统当中附增的一个新的功能。
我们都知道,属性动画的机制已经不是再针对于View而进行设计的了,而是一种不断地对值进行操作的机制,它可以将值赋值到指定对象的指定属性上。但是,在绝大多数情况下,相信大家主要都还是对View进行动画操作的。谷歌也是意识到了这一点,没有为View的动画操作提供一种更加便捷的用法确实是有点太不人性化了,于是在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。
2、用法介绍
我们先来回顾一下之前给一个View对象添加动画的写法吧,比如我们想要让一个TextView从常规状态变成透明状态,就可以这样写:
ObjectAnimator.ofFloat(textview, "alpha", 0f).start();
好像也不怎么复杂,相比于补间动画要创建对应动画的对象,并且还不能连缀写已经好多了,但确实也不怎么容易理解。我们要将操作的view、属性、变化的值都一起传入到ObjectAnimator.ofFloat()方法当中,虽然看上去也没写多少代码,但这不太像是我们平时使用的面向对象的思维。
接下来我们就来看一下如何使用ViewPropertyAnimator来实现同样的效果,ViewPropertyAnimator提供了更加易懂、更加面向对象的API,如下所示:
textview.animate().alpha(0f);
是不是简单的难以置信,animate()方法就是在Android 3.1系统上对View新增的一个方法,这个方法的返回值是一个ViewPropertyAnimator对象,也就是说拿到这个对象之后我们就可以调用它的各种方法来实现动画效果了,这里我们调用了alpha()方法并转入0,表示将当前的textview变成透明状态。
同时它也可以轻松地将多个动画组合起来,比如我们想要将当前的textView移动到(500,500)这个坐标上:
textview.animate().x(500).y(500);
是不是既简单有好理解,可以看到ViewPropertyAnimator是支持连缀的,只要用上面所写方式,那么被连缀的所有动画就会同时执行。
同样无论是设置时间还是设置 Interpolator,都可以通过连缀的方式,这一点和 ObjectAnimator 是一样的:
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
3、注意
除了用法之外,关于ViewPropertyAnimator有几个细节还是值得注意的:
- 整个ViewPropertyAnimator的功能都是建立在View类新增的animate()方法之上的,这个方法会创建并返回一个ViewPropertyAnimator的实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的。
- 在使用ViewPropertyAnimator时,我们自始至终没有调用过start()方法,这是因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。
- ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完一个方法之后可以直接连缀调用它的另一个方法,这样把所有的功能都串接起来,我们甚至可以仅通过一行代码就完成任意复杂度的动画功能。
4、性能提升
即使是在一个简单的 alpha 动画上,使用 ViewPropertyAnimator 也是性能完胜的。 ViewPropertyAnimator 并不使用反射或者 JNI 技术。例如,alpha() 方法是在每一帧直接改变 View 对象的 alpha 属性值。
ViewPropertyAnimator 的另一个性能上的胜利来自于它可以将多个动画结合起来,前面我们说过 ViewPropertyAnimator 可以把多个动画连缀,现在我们拿它和 ObejctAnimator实现组合的方法去比较一下:
ObjectAnimator animX = ObjectAnimator.ofFloat(textView, "x", 500f);
ObjectAnimator animY = ObjectAnimator.ofFloat(textView, "y", 500f);
AnimatorSet animSet = new AnimatorSet();
animSet.playTogether(animX, animY);
animSet.start();
如果我们要实现举例的把TextView移动到(500,500)这个坐标上,用AnimatorSet这个方法,就需要创建两个动画,并通过 AnimatorSet 来让它们同时播放。这种方式在处理上存在着额外的开销,因为它需要额外创建一个 AnimatorSet 并且在同时执行两个动画。
再看看使用 PropertyValuesHolder 的情况:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 500f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 500f);
ObjectAnimator.ofPropertyValuesHolder(textView, pvhX, pvyY).start();
这种解决方案避免了多个 Animator 的额外开销,在 ViewPropertyAnimator 之前它是一个正确的方法。并且代码看起来不是太糟。但是使用 ViewPropertyAnimator 的话,它会更加简单:
textview.animate().x(500).y(500);
这段代码更加简单也更加易读。而且它和上面使用 PropertyValuesHolder 的例子一样具有单一 Animator 的优点,因为 ViewPropertyAnimator 在内部只运行一个 Animator 来使所有指定的属性做动画。所以如何使用就一目了然啦。
5、方法介绍
Method | Discription |
---|---|
alpha(float value) | 设置透明度,value表示变化到多少,1不透明,0全透明 |
scaleX(float value) | 设置X轴方向的缩放大小,value表示缩放到多少,1代表正常规格。小于1代表缩小,大于1代表放大 |
scaleY(float value) | 设置Y轴方向的缩放大小,value表示缩放到多少,1代表正常规格。小于1代表缩小,大于1代表放大 |
translationX(float value) | 设置X轴方向上的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置,即移动到哪里 |
translationY(float value) | 设置Y轴方向上的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置,即移动到哪里 |
rotation(float value) | 控制View对象围绕支点进行旋转,rotation针对2D旋转 |
rotationX(float value) | 控制View对象围绕X支点进行旋转,rotationX针对3D旋转 |
rotationY(float value) | 控制View对象围绕Y支点进行旋转,rotationY针对3D旋转 |
x(float value) | 控制View对象相对于它父容器左上角坐标在X轴方向的最终位置 |
y(float value) | 控制View对象相对于它父容器左上角坐标在y轴方向的最终位置 |
void cancel() | 取消当前正在执行的动画 |
setListener(Animator.AnimatorListener listener) | 设置监听器,监听动画的开始,结束,取消,重复播放 |
setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) | 设置监听器,监听动画的每一帧的播放 |
setInterpolator(TimeInterpolator interpolator) | 设置插值器 |
setStartDelay(long startDelay) | 设置动画延长开始时间 |
setDuration(long duration) | 设置动画执行的时间 |
withLayer() | 是否开启硬件加速 |
withStartAction(Runnable runnable) | 设置用于动画监听开始(Animator.AnimatorListener)时运行的Runnable任务对象 |
withEndAction(Runnable runnable) | 设置用于动画监听结束(Animator.AnimatorListener)时运行的Runnable任务对象 |
这里就讲下withStartAction() 和withEndAction() 方法,其它的没什么好提的,跟其它动画框架的内容是一样的。
withStartAction() 是谷歌在SDK12上提出的,withEndAction() 则是在SDK16上提出的,分别是用于在动画前,和动画后执行一些操作。当然其实用 setListener() 也可以实现同样的效果。
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private float mScreenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
DisplayMetrics outMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
mScreenHeight = outMetrics.heightPixels;
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
imageView.animate()
.alpha(0)
.y(mScreenHeight / 2).setDuration(1000)
.withStartAction(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "animation start", Toast.LENGTH_SHORT).show();
}
}).withEndAction(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setY(0 + 32);
imageView.setAlpha(1.0f);
}
});
}
});
}
});
}
}
结束语:本文仅用来学习记录,参考查阅。