接着上篇Android中的动画(XML方式)实践(逐帧动画与补间动画)来学习下属性动画。
我们知道属性动画是API11(android3.0)新加入的特性。其实部分常用的动画效果靠传统动画也可解决,那么:
为什么要引入属性动画
我们先看看传统动画能实现什么:帧动画功能单一不必多说(图片顺序播放),再看补间动画---可对View进行一系列的效果变换(淡入淡出、缩放、平移、旋转)。看上句加粗部分便能明白,补间动画有两个局限性:1.作用对象单一(View)2.效果变换固定。举几个例子:1.我要给TextView加个动画,从当前宽度增加到300像素。这个用补间动画是实现不了的,有人说可以通过x方向缩放实现,但你要那么做了,就会把里面的文字也在x方向拉伸,而不仅仅改变textview宽度(改变了整个textview)。2.我要对一个imageview(想象一下:显示一个大美女的那种)做个3D旋转,这个用补间动画也是做不了的。还有一点可以说是补间动画最坑的地方:它只改变View的显示,而没改变View的属性。这就会造成类似下图那种好玩(cao dan)的现象:
那属性动画就能实现这些补间动画不能实现的效果吗?是的!连带也把补间动画能实现的效果也给实现了。是不是很强势。。。
开始使用属性动画
在开始使用之前,我们首先要知道它能用在哪?否则会闹出类似拿剁骨刀削苹果的笑话。
之所以称之为属性动画,是因为它可以对任意对象的属性进行动画变换。也就是说:只要对象有这个属性,你都能用属性动画来实现动画(java中有万物皆对象一说,可见属性动画的适用范围之广)。大致含义搞清楚了,下面就开始写几个例子,学习一下(以属性动画中常用的三个类:ObjectAnimator(继承自ValueAnimator),ValueAnimator,AnimatorSet(动画集合)为例):
1.以view自身中心为旋转点顺时针旋转半圈(ObjectAnimator实现):
看看效果图:
2.对view背景色(透明-橘色-亮绿)做改变(ValueAnimator实现):
效果图:
3.对view同时做渐变,缩放,旋转操作(AnimatorSet实现):
运行效果:
上面分别使用ObjectAnimator,ValueAnimator和AnimatorSet实现了一些效果。那下面一个个依次的详细说一下:
ObjectAnimator
第一个例子就足以证明一点:用ObjectAnimator实现动画真特么太简单了,一行代码就可以实现旋转效果(当然不只于旋转)。好吧,我们进去ObjectAnimation类看看:
看看我们上面例子中用到了ofFloat方法,不禁要问:里面的设置的参数都是些什么鬼?现在就来找到那个方法,看看具体是怎么回事:
我们可以看到方法里面创建并了返回了一个ObjectAnimator对象。具体看一下各参数的含义吧:第一个参数target :表示要执行动画的对象;第二个参数propertyName:对象被改变属性对应的属性名(这个后面还要详细说);第三个可变参数values:可以理解为动画过程设置的属性值。
参数values展开来说:
当我们调用方法指定values为一个值,表示动画结束时属性值(如上例);两个值则表示开始值和结束值;三个或三个以上,其中第一个和最后一个值分别表示开始和结束属性值,中间则表示动画过程中任意属性值。说得再多还不如试试更容易理解:values设为一个值的我们已经试过,现在来试试设置多个值的:
其实ObjectAnimator类似ofFloat的方法还有ofInt,ofArgb,乃至ofObject等,这些方法都是设置动画作用的对象、改变的属性和动画开始,结束还有中间(任意个数)的属性值。
参数propertyName属性名详解:
还拿ofFloat方法来说,第一个参数target 含义好理解,第三个参数values也说过了,那第二个参数propertyName到底怎么理解,是随便写一个字符串就行了吗?显然不是!上面ofFloat函数的注释已经做了解释:第一个参数target对象应该有个类似方法名为setName作用域为public的方法,第二个参数propertyName即可指定对应属性名为name。
就拿我们上面旋转来说:... .ofFloat(mImageView,"rotation",...) 我们指定属性名为“rotation”,那肯定ImageView或其父类存在 setRotation 方法--果不其然在ImageView 父类View中找到了该方法:
然后还会发现一个好玩的现象--我们试着换种写法:
把ofFloat换成ofInt,后面参数改为int型,发现会报错。大概可以理解为没在ImageView(或其父类)中找到setter方法public void setRotation(int arg);我们从源码中看到setRotation方法参数是float类型的,所以用ofFloat方法,它不会报错,因为能找到相应类型参数的setter方法。也就是说:方法ofXxx中的类型Xxx应该与指定属性对应setter方法的参数值一致。这样一说,我们我就会理解为什么可变参数values是指定属性值的。
当然也并不是只要作用对象target有setXxx方法,我们对target属性xxx做动画,就能生效:这里需要满足两个条件:
1.如果动画没有传递初始值,那么target中需要提供getXxx方法,因为系统要自己去取属性的初始值,若不满足这点,程序会崩溃。比如:
由于ImageView中没有getImageResource方法但存在setImageResource,如上图代码段,没指定初始值,这样的话,运行就会直接crash掉。
2.target对象的setXxx对属性xxx所做的改变得通过一些途径反映出来。就比如带来UI上的改变。这很好理解,要不能在UI上给用户反映出来,那用户就没法看到动画效果。看看这句:
imageview有setMaxWidth和getMaxWidth方法,但maxwidth属性对imageview在外在表现上没有影响的,所以我们看不到有任何效果。
对第一点还需要多说一下,不能错误理解为target没有getXxx方法,运行代码就会崩溃,是有两个条件(target没有提供getXxx方法+没有设置初始值)看看下面的代码就可以明白了(同样针对imageResourse):
运行一下,是不会报错的:
PropertyValuesHolder
我们能不能仅仅使用一个动画(ObjectAnimator)就实现多个效果呢?比如说旋转,缩放,渐变,位移几个效果一起?(尽管这种“多效果”用AnimatorSet可轻松实现)。
问题先留在这里,待会再解答。
上面有提到过ObjectAnimator类似ofFloat的方法还有ofInt,ofArgb,ofObject等,还有一个方法叫做ofPropertyValuesHolder:
函数返回值与ofFloat等一致为ObjectAnimator对象,参数target为动画作用对象,那后面的values参数:PropertyValuesHolder类型的可变数组是什么意思。这个PropertyValuesHolder是什么东西,看看该类:
还是看注释:注释第一句 看类名(Property-Values-Holder)其实就可以理解;第二句刚好可以回答上面提出带答的问题。所以答案显而易见--能。那就实现一个看看吧:
另:PropertyValuesHolder常用创建实例的函数有下面四种:
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values)
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
现在这里就不展开来说了。
未完下次再续。。。
由于作者水平有限,有纰漏或错误处还望大家批评指正,谢谢!
感谢:
任玉刚 《Android开发艺术探索》