Android基础夯实--重温动画(五)之属性动画ObjectAnimator
详解
只有⼀种真正的英雄主义
⼀、摘要
ObjectAnimator是ValueAnimator的⼦类,它和ValueAnimator⼀样,同样具有计算属性值的功能,但对⽐ValueAnimator,它会更加容易使⽤,因为它不再需要设置来监听值的变化,因为这个⼯程对于ObjectAnimator来说,是⾃动的。这篇⽂章主要通过详细讲解ObejctAniamtior,加深⼤家对属性动画的认识,让我们对于动画的技巧掌握得更扎实。
如果你想了解更权威的解释,可以查看官⽅⽂档:。
本⽂主要对ValueAnimator做介绍,如果⼤家有兴趣,可以继续阅读本动画系列其他相关⽂章,作者也在不断更新完善相关内容,希望⼤家可以指出有误之处。
⼆、概述
在上节我们知道了在属性动画中,ValueAnimator是通过监听值的变化,然后实现控件的动画播放。在代码过程中,是通过设置初始值、结束值和动画时间,然后通过加速器返回当前的进度的,再经过Evaluator根据进度计算出具体的值,然后我们在⾥⾯不断监听拿到这个值,然后修改控件的属性值,从⽽实现动画。
ObjectAnimator作为ValueAnimator的⼦类,所以ValueAnimator的很多⽅法,在ObjectAnimator中也能使⽤,但是ObjectAnimator覆写了⽗类的⼏个⽅法,如ofInt(),ofFloat(),ofArgb()等。它和ValueAnimator同样也是⾸先设置初始值、结束值和动画时长,但是同时也绑定了⽬标控件和属性然后通过加速器返回当前的进度的,再经过Evaluator根据进度计算出具体的值,最后根据属性拼接set函数并反射调⽤,并将当前值作为参数传⼊,实现动画。
对⽐这两个Animator,ObjectAnimator是对ValueAnimator的再封装,它的封装帮助我们避免了使⽤Listener的⿇烦,更加精简了代码,使开发者可以更加专注动画的逻辑代码。
2.1 差异
以下通过Demo来对⽐ObjectAnimator和ValueAnimator的区别,我们同样使⽤ObjectAnimator和ValueAnimator实现同样的效果,控件⽔平⽅向上的不断左右移动,最后返回原点(如下图)。
通过上⼀节的学习,我们可以轻松写出ValueAnimator实现的代码:
ValueAnimator animator = ValueAnimator.ofFloat(0, 300, -100, 200, -50, 0);
animator.setDuration(2000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBinding.image.setTranslationX((Float) AnimatedValue());
}
});
在ObjectAnimator中,我们可以⽤更简单的代码来实现:
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "translationX", 0, 300, -100, 200, -50, 0);
animator.setDuration(2000);
animator.start();
通过对⽐,我们可以看到,ObjectAnimator在实现动画上的代码上会更加简洁,但是实现的效果都是⼀样的,之所以是这样,是因为ObjectAnimator覆写了ValueAnimator 的ofFloat⽅法,并对其进⾏了封装。所以,我们看到ObjectAnimator实现代码上都减少了Listener代码的编写。
⼤家第⼀次看到ObjectAnimator可能看到代码⽐较陌⽣,上⾯的⼀段代码什么意思呢?⾸先我们看到第⼀个参数,就是我们动画的⽬标控件,也就是这个动画要让哪个控件来实现,我这⾥是DATABinding的⼀个写法,其实就是我们findViewById得到的⼀个view,⾮常的简单;第⼆个参数是动画要实现的效果,我这⾥是translationX,即⽔平x⽅向上的位移;第三个参数为可变参数,即动画变化过程的系列值,跟ValueAnimator是⼀样的意思。
2.2 propertyName
在2.1的介绍过后,⼤家可能还会有疑问,对于上⾯ofFloat的第⼆个参数是⼀个字符串常量,这个字符串常量我们是怎么获取的呢?其实,在ObejctAnimator中,⽆论ofFloat,ofInt,ofArgb等⽅法,都有⼀个叫做propertyName的参数,也就是我们上⾯对应的第⼆个参数,这个参数是⼀个字符串,之所以动画会实现该字符串的效果是因为ObjectAnimator通过反射机制,到了ImageView中的setTranslationX()这个⽅法,然后每个⼗⼏ms就调⽤这个⽅法,并把我们的变化的值传到⾥⾯去,从⽽实现动画效果。
所以到这⾥⼤家可以知道,当我们需要⽤ObjectAnimator实现⼀个控件的动画效果时,我们⾸先需要做的就是在这个控件中到对应的setXXX()驼峰式写法的⽅法,只有控件拥有相应的setXXX()⽅法,我们传⼊的propertyName参数才起到作⽤。那么这时候就有同学会问,类似Demo中的“translationX”,它对应的⽅法是setTranslationX(),那我们应该传⼊“TranslationX”还是“translationX”,其实都是可以的,因为它内部封装在使⽤反射机制调⽤⽅法时,涵盖了两种写法,所以第⼀个字母可以⼤⼩写,但是后⾯的字母必须全部对应⼤⼩写。
其次还要同学会疑问,我们怎么知道在实例化ObjectAnimator时应该通过ofFloat还是ofInt还是其他呢?其实跟我们的ValueAnimator⼀样,我们调⽤哪个⽅法来实例化都是要考虑我们要改变的控件哪个属性的。例如,我们上⾯的例⼦是改变⽔平⽅向上的位移,那么ObjectAnimator最终是调⽤控件的setTranslationX(float translationX),我们可以看到传⼊参数是float型,那么毫⽆疑问,我们这⾥需要使⽤ofFloat了,其它以此类推。下⾯给⼤家举例⼀些常⽤的propertyName。
2.2.1 Scale
View中关于伸缩变化(Scale)有以下两个⽅法:
public void setScaleX(float scaleX):X⽅向上伸缩。
public void setScaleY(float scaleY):Y⽅向上伸缩。
所以对应代码为:
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "scaleX", 0f, 1.5f, 2f, 1.5f, 0f, 0.5f, 0.2f, 1f);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.setDuration(2000);
animator.start();
View view = new View(MyObjectAnimator.this);
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "scaleY", 0f, 1.5f, 2f, 1.5f, 0f, 0.5f, 0.2f, 1f);
animator.setDuration(2000);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.start();
2.2.2 Translation
View中关于位置变化(Translation)有以下两个⽅法:
public void setTranslationX(float translationX):X轴上位移。
public void setTranslationY(float translationY):Y轴上位移。
public void setTranslationZ(float translationZ):设置阴影。
所以对应代码为:
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "translationX", 0, 100, -100, 0);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.setDuration(2000);
animator.start();
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "translationY", 0, 100, -100, 0);
animator.setDuration(2000);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.start();
2.2.3 Alpha
View中关于透明度变化(Alpha)有以下⽅法:
public void setAlpha(float alpha);
所以对应代码为:
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "alpha", 0, 0.5f, 1.0f);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.setDuration(2000);
animator.start();
2.2.4 Rotation
View中关于⾓度变化(Rotation)有以下⽅法:
public void setRotation(float rotation):关于Z轴旋转。
public void setRotationX(float rotationX):关于X轴旋转。
public void setRotationY(float rotationY):关于Y轴旋转。
所以对应代码为:
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "rotation", 0, 180, 0, -180, 0);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.setDuration(3000);
animator.start();
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "rotationX", 0, 180, 0, -180, 0);
animator.setDuration(3000);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.start();
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "rotationY", 0, 180, 0, -180, 0);
animator.setDuration(3000);
mBinding.image.setScaleType(ImageView.ScaleType.CENTER_CROP);
animator.start();
2.2.5 图⽚变化
View中关于图⽚的其中⼀个⽅法是setBackgroundResource,同样我们也可以通过ObjectAnimator来实现⼀个图⽚变化的效果:public void setBackgroundResource(@DrawableRes int resid)
对应代码为:
ObjectAnimator animator = ObjectAnimator.ofInt(mBinding.image, "backgroundResource", R.drawable.a1, R.drawable.a2, R.drawable.a3);
animator.setDuration(2000);
animator.start();
其实还有很多set⽅法可以可以实现动画效果,这⾥就不再⼀⼀列举,⼤家有兴趣可以⾃⼰去深⼊研究,你会发现ObjectAnimator可以⾮常简单快捷地实现动画效果。
2.3 实例化ObjectAnimator⽅法
在ValueAnimator中,我们知道实例化并不是通过new⼀个对象出来,⽽是通过ofInt,ofFloat,ofObject等⽅法。在ObjectAnimator中同样如此,因为ofInt,ofFloat,ofObject等⽅法的内部帮我们封装了实例化过程,所以我们可以直接调⽤来拿到⼀个实例化的对象。在ObjectAnimator中,⼤概有以下⼏种实例化⽅法:
2.3.1 ofInt()
通过ofInt()来实例化对象,那么属性值必须为int型,通常我们通过ofInt可以实现很多动画,例如实现颜⾊渐变等;ofInt()也有⼏个重载函数,这⾥介绍其中⼀个:
ofInt(Object target, String propertyName, values):对⽬标对象T的property属性值进⾏改变。
例如颜⾊值的变化。
ObjectAnimator animator = ObjectAnimator.ofInt(mBinding.image, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(4000);
animator.start();
2.3.2 ofFloat()
ofFloat()来实例化对象,那么属性值必须为float型,通常我们通过ofFloat可以实现很多动画,例如实现位置变化等;ofFloat()也有⼏个重载函数,这⾥介绍其中⼀个:
ObjectAnimator ofFloat (Object target, String xPropertyName, String yPropertyName, Path path)::对⽬标对象T的property属性值进⾏改变。
例如实现⼀个贝塞尔曲线:
Path path = new Path();
path.quadTo(800, 200, 800, 800);
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.image, "x", "y", path);
animator.setDuration(4000);
animator.start();
2.3.3 ofArgb()
我们在ValueAnimator中已经提到了ofArgb()可以帮助我们实现颜⾊的渐变效果,这⾥同样是可以通过ofArgb()来实现动画效果。上⾯我们已经在ofInt⾥⾯实现了颜⾊渐变,但是代码稍多,所以Google在API LEVEL 21之后增加了这个⽅法ofArgb()。通过这个⽅法我们更容易地实现颜⾊演变,因为它⾥⾯封装了对ArgbEvaluator的使⽤,实现2.3.1的效果,⼤家可以对⽐⼀下代码:
ObjectAnimator animator = ObjectAnimator.ofArgb(mBinding.image, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
animator.setDuration(4000);
animator.start();
2.3.4 ofPropertyValuesHolder()
认真的同学都会发现,在ValueAnimator和ObjectAnimator中,都有⼀个实例化⽅法,就是ofPropertyValuesHolder()⽅法,由于在ObjectAnimator中使⽤更为⼴泛,所以这⾥以ObjectAnimator的ofPropertyValuesHolder为例⼦,当⼤家懂了之后,那么⼤家对ValueAnimator的ofPropertyValuesHolder也应该理解了。
html animation属性在ObjectAnimator中,我们可以通过
ObjectAnimator ofPropertyValuesHolder (Object target,
< values)
来实例化⼀个ObjectAnimator。
我们可以看到,它和我们其他的实例化⽅法差不多,都需要设置⼀个target(⽬标控件),还有⼀组Pro
pertyValuesHolder类型的值,但是不需要设置属性,target我们知道了,是要实现动画的控件,那么PropertyValuesHolder是什么呢?我们来看⼀下官⽅⽂档:
This class holds information about a property and the values that that property should take on during an animation.
PropertyValuesHolder objects can be used to create animations with ValueAnimator or ObjectAnimator that operate on several different properties in parallel.
什么意思呢?
这是⼀个包含⼀个属性信息的类,并且它的值应该⽤到⼀个动画⾥⾯。PropertyValuesHolder对象可以配合ValueAnimator和
ObjectAnimator来实现不同属性的并⾏的动画。
听起来有点别扭,也就是说,当我们需要实现⼀个包含多种属性的同时播放的动画时,我们就可以使⽤ofPropertyValuesHolder来实例化⼀个Animator,当然,拥有⼀个属性时也是可以的,为什么这么说?ofFloat()的内部实现其实就是将传进来的参数封装成PropertyValuesHolder实例来保存动画状态。所以可见PropertyValuesHolder是多么有⽤了吧。
在PropertyValuesHolder这个类⾥⾯,同样也有ofInt(),ofFloat(),ofKeyframe()等⽅法来实例化,举个例⼦:
代码也⾮常简单:
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 90, -90, 45, -45, 60, -60);
PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xff55aa11, 0xff115633, 0xff123344, 0xffaabbcc);
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat("ScaleX", 1f, 1.1f, 1.2f, 1.5f, 1.8f, 1.5f, 1.2f, 1.1f, 1);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofFloat("ScaleY", 1f, 1.1f, 1.2f, 1.5f, 1.8f, 1.5f, 1.2f, 1.1f, 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mBinding.image, rotationHolder, colorHolder, scaleXHolder, scaleYHolder);
animator.setDuration(3500);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
通过代码我们可以知道,通过ofPropertyValuesHolder来实例化,其实就是将不同动画效果分配到⼀个个PropertyValuesHolder中去,然后把多个不同的PropertyValuesHolder对象在初始化时传⼊,最终实现多个效果并⾏播放。
2.3.4 其他实例化⽅法
在ObjectAnimator中提供了⾮常丰富的实例化⽅法,除了以上三个之外,在API LEVEL 21之后,Google推出了更多的实例化⽅法,例如:
ofMultiFloat()
ofMultiInt()
ofObject()
ofPropertyValuesHolder()
在上⾯三个(2.3.1-2.3.3)不⾜以解决我们需求的时候,我们可以到官⽅⽂档参考这三个⽐较新的实例化⽅法,它们也是为了简化操作⽽进⾏
了更⾼度的封装,所以这也有助于帮助我们⽤更少的代码来实现动画。
总结
ObjectAnimator作为ValueAnimator的⼦类,在代码上对ValueAnimator进⾏了进⼀步的封装,使我们在⽇常使⽤中更加简单,但是正是因为封装,使得我们在⼀些特殊情况下使⽤ObjectAnimator使⽤上还是有⼀定的局限性,所以在⼤家掌握了ValueAnimator和ObjectAnimator的基本使⽤后,还需要⾃⼰通过写⼩Demo来加深和进阶使⽤,这样在我们⽤到时⽅能得⼼应⼿。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论