上篇文章中,我们曾经提到过,ObjectAnimator实现动画效果是,可以这样写:
ObjectAnimator
.ofFloat(myView, "rotationX", 0.0F, 360.0F)
.setDuration(500)//
.start();
上篇文章说过,第二个参数是指属性的名字,例如rotationX,但是如果我们随便写个值的话,轻者则无动画效果,重者可能会崩溃,为什么呢?今天就来说说原因
举个例子,给Button加一个动画,让这个Button的宽度从当前宽度增加到500,也许你可能会这样想:
ObjectAnimator
.ofFloat(mButton, "width", 500)
.setDuration(500)//
.start();
可是,当运行时,你会发现没有任何效果,为什么呢?其实,我们如果知道属性动画的原理,就很容易理解了
属性动画原理
属性动画要求做动画效果的属性需要提供get和set的方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递set方法的值都不一样,随着时间的推移,越来越接近最终值,所以,总的来说,需要动画生效,需满足两个条件:
(1)对象的属性必须提供set和get方法
(2)set方法可以带来效果,比如我setWidth后,宽度会带来UI的变化,否则你也看不出变化是吧
在回到我们的例子上,有人可能会说,Button提供了setWidth方法啊,为什么还是不行呢?别着急,我们来看看setWidth的源码:
/**
* Makes the TextView exactly this many pixels wide.
* You could do the same thing by specifying this number in the
* LayoutParams.
*
* @see #setMaxWidth(int)
* @see #setMinWidth(int)
* @see #getMinWidth()
* @see #getMaxWidth()
*
* @attr ref android.R.styleable#TextView_width
*/
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
Button是继承TextView,setWidth是TextView和子类专属的方法,我们从源码中不难看出,它的作用不是设置宽度,而是设置最大宽度和最小宽度,也就是说跟我们想要设置Button宽度是两个东西,具体来说,TextView的宽度对应的是xml中的android:layout_width,而setWidth对应的是xml中的android:width,所以没有动画效果也是正常的,那如果我的需求就是类似这样的情况,我们怎么办呢?别着急,谷歌早就帮我们想好了
类似这样的情况,官方文档告诉我们有3种解决方法:
(1)给属性加上get和set的方法
(2)用一个类来封装原始对象,并提供get和set方法
(3)采用ValueAnimator,监听动画过程,自己实现属性的变化
首先我们来分别看看这几个方法:
方法1:
我们首先就可以排除了,我们不可能去修改Button的源码,给它加上get和set方法
方法2:
是一个很有用的解决方法,也是最普遍的,很方便也很好理解,我们看看例子
public class MyButton {
private View mView;
public MyButton(View view) {
this.mView = view;
}
public int getWidth() {
return mView.getWidth();
}
public void setWidth(int width) {
mView.getLayoutParams().width = width;
mView.requestLayout();
}
}
我们先定义一个类来封装我们的Button,然后提供get和set方法,再来我们就可以调用了:
MyButton button =new MyButton(new Button(this));
ObjectAnimator.ofInt(button,"width",500).setDuration(500).start();
没错,就是如此简单
方法3
如果大家对ValueAnimator熟悉的话就知道,它不作用于任何对象,也就是说直接使用它是没有动画效果的,我们需要它对一个值做动画,然后监听变化过程,在变化过程中修改我们对象的属性值,就相当于我们对象做动画了
ValueAnimator animator = ValueAnimator.ofInt(1, 100);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取当前动画进度值,1-100之间
int value = (int) animation.getAnimatedValue();
//获取当前进度占整个动画过程比例,0-1之间
float fraction = animation.getAnimatedFraction();
//直接调用估值器,通过比例计算宽度,然后再赋值给Button
IntEvaluator evaluator =new IntEvaluator();
mButton.getLayoutParams().width=evaluator.evaluate(fraction,mButton.getWidth(),500);
mButton.requestLayout();
}
});
animator.setDuration(500).start();
它在500ms内将一个数从1到100,然后动画的每一帧都会回调onAnimationUpdate方法,因此,我们可以获取当前的值和当前值所占的比例,在计算出Button当前的宽度应该是多少,比如时间过了一半,当前值是50,比例是0.5,假设Button的起始宽度是100,最终宽度是500,那么Button增加的宽度也应该占总增加宽度的一半,总增加的宽度是500-100=400,所以这时候Button应该增加的宽度是400*0.5=200,那么当前Button的宽度应该为初始宽度+增加宽度(100+200=300),这个计算IntEvaluator 估值器已经帮我们实现了,因此我们直接用就可以了
注意事项
动画可以为我们带来很多炫酷的效果,但同时也有很多需要注意的地方,主要分为这几类:
OOM
这个问题主要出现在帧动画中,因为帧动画是用多张图片轮播实现动画效果,那么如果图片过大就很容易出现OOM
内存泄漏
在属性动画中,有一类无限循环的动画,这类动画需要在Activity退出的时候及时停止,否则会导致无法释放从而导致内存泄漏,View动画没存在这问题
兼容性
动画是3.0以下的系统兼容性会存在问题,因此需要做好兼容工作
View动画的问题
View动画是对view的影像做动画,并不是真正的去改变view的状态,因此有时候会出现动画完成后view无法隐藏的现象,即setVisibility(GONE)失效,这时候只要调用view.clearAnimation()清除view动画即可
不要使用px
在进行动画过程中,单位尽量用dp,使用px可能导致在不同设备上效果不一样
动画过后交互
view动画还是属性动画效果完成后,view的位置是不会发生改变的,也就是说view的位置还是在原来的位置,尽管view的视觉已经改变,位置是不发生变化,仅仅只是视觉的变化
好了,这篇文章到这就结束了