ObjectAnimator,通过设置改变对象的属性来实现动画效果,常用的方法有这么几种,ofFloat()、ofInt()、ofObject()、ofArgb()、ofPropertyValuesHolder(),具体含义及使用我们在下面的实例中进行讲解。
使用ObjectAnimator也是可以轻松的实现平移、缩放、旋转、透明度这几种动画效果的,与补间动画的使用效果是一样的,那就先来看看这几种常用的动画是怎么实现的。
工程代码里就是一个ImageView控件和Activity,
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/head" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/head" /> </RelativeLayout>
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_object_animator); head = (ImageView) findViewById(R.id.head); }
我们这里就是对ImageView控件head实现动画效果,本质就是改变head的属性。
1.平移(translate)
// 平移 private void translateAnimation() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0.0f); objectAnimator.setDuration(2000); objectAnimator.setRepeatCount(Animation.INFINITE); objectAnimator.setRepeatMode(Animation.RESTART); objectAnimator.start(); }
这段代码就是用来实现控件的平移,我们来逐行分析这段代码。
ofFloat方法的函数原型,
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
参数含义:
target:动画操作的对象,我们这里的操作对象就是ImageView控件head
propertyName:属性名称,这里的"translationX"属性值意思就是在水平方向移动,如果是"translationY"就是在垂直方向移动,下面讲到的其动画效果也是这个意思,这个属性值跟我们在xml文件中设置属性值得名称是一致的,比如"android:translationY"。values:动画过渡值,过渡值可以有一个到N个,如果是一个值的话,就默认是这个动画过渡值的结束值,如果有N个值,动画就在这N个值之间过渡,如本例中有三个过渡值"0.0f, 350.0f, 0f",意思就是从当前位置向右滑到350的位置,再滑到位置0,即初始位置。
然后是动画的设置,
objectAnimator.setDuration(2000);//动画的时间间隔 objectAnimator.setRepeatCount(Animation.INFINITE);//重复次数 objectAnimator.setRepeatMode(Animation.RESTART);//重复模式
最后start,动画就开始执行了
objectAnimator.start();</span>
2.缩放(scale)
// 缩放 private void scaleXAnimation() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "scaleX", 1.0f, 1.5f); objectAnimator.setDuration(2000); objectAnimator.setRepeatCount(Animation.INFINITE); objectAnimator.setRepeatMode(Animation.RESTART); objectAnimator.start(); }
3.旋转(rotate)
// 旋转 private void rotateAnimation() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "rotationX", 0.0f, 90.0f,0.0F); objectAnimator.setDuration(2000); objectAnimator.setRepeatCount(Animation.INFINITE); objectAnimator.setRepeatMode(Animation.RESTART); objectAnimator.start(); }
4.透明度(alpha)
// 透明度 private void alphaAnimation() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "alpha", 1.0f, 0.3f, 1.0F); objectAnimator.setDuration(2000); objectAnimator.setRepeatCount(Animation.INFINITE); objectAnimator.setRepeatMode(Animation.RESTART); objectAnimator.start(); }
可能我们也注意到了,在缩放效果的实例中,图片被放大后就保持当前的状态,没有变为初始的样子,这是因为我们在设置过渡值是最终的状态是1.5f,就放大到1.5倍,并没有让它回到原来的状态,从这点就可以看出,属性动画是真真切切的可以改变控件的属性的,这是与补间动画的最大不同,补间动画在动画结束后都将回到初始状态。所以说,属性动画的使用就显得更加的灵活。至于上面的其他三个实例,最终回到初始状态,是因为我们在设过渡值的时候最终状态设定的就是初始状态。
<TextView android:id="@+id/txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:layout_centerInParent="true" android:textSize="32sp" android:text="测试颜色变化"/>
int startColor = 0xffff0000; int endColor = 0xff00ff00; ObjectAnimator objectAnimator4 = ObjectAnimator.ofObject(txt, "textColor", new TypeEvaluator() { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int)((startA + (int)(fraction * (endA - startA))) << 24) | (int)((startR + (int)(fraction * (endR - startR))) << 16) | (int)((startG + (int)(fraction * (endG - startG))) << 8) | (int)((startB + (int)(fraction * (endB - startB)))); } }, startColor, endColor); objectAnimator4.setDuration(3000); objectAnimator4.start();
ofObject方法的原型
public static ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
ObjectAnimator objectAnimator2 = ObjectAnimator.ofArgb(txt, "textColor", 0x000, 0x00FF00); objectAnimator2.start();
1.setInterpolator():设置动画插值
控制动画的变化速率,系统中定义了好几种Interpolator:
AccelerateDecelerateInterpolator--先加速后减速AccelerateInterpolator--加速
DecelerateInterpolator--减速
2.setDuration():设置动画执行时间,动画时间以毫秒为单位(ms)
3.setRepeatCount():设置动画重复次数
大于0的值就代表重复几次,如果需要无限循环,设为-1,上面的Animation.INFINITE是系统给的常量,值为-1,代表无限循环,我们建议使用这个常量,如果设为0呢?也是执行一次。
4.setRepeatMode():设置动画重复模式
5.setStartDelay():设置动画延时操作,也是以毫秒为单位(ms)
6.setTarget():设置动画的对象
操作对象,上面的例子中将动画对象通过ofXXX方传递,如果需要改变动画对象,但动画效果不变,我们可以使用该方法来设置。
7.setEvaluator():设置动画过度的评估者,即设置TypeEvaluator对象,后面会详细介绍objectAnimator.setTarget(txt);//将动画对象head变为txt
after(Animator anim)--将现有动画插入到传入的动画之后执行
after(long delay)--将现有动画延迟指定毫秒后执行
before(Animator anim)--将现有动画插入到传入的动画之前执行
with(Animator anim)--将现有动画和传入的动画同时执行
playSequentially(Animator... items)--依次执行
private void multiAnimation() { ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(head, "scaleX", 1.0f, 2.5f, 1.0f); ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(head, "scaleY", 1.0f, 2.5f, 1.0f); ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(head, "rotationX", 0.0f, 90.0f,0.0F); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(objectAnimator1).with(objectAnimator2).with(objectAnimator3); animatorSet.setDuration(2000); animatorSet.start(); }
让动画同时执行除了使用with方法外,还可以用playTogether方法,
animatorSet.playTogether(objectAnimator1, objectAnimator2, objectAnimator3);其他几种就不在演示了。
这种方式只能多个动画一起执行,不同设置先后顺序。
private void multiAnimation2() { PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.5f, 1.0f); PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.5f, 1.0f); PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f,0.0F); ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(head, valuesHolder, valuesHolder1, valuesHolder2); objectAnimator.setDuration(2000); objectAnimator.start(); }
private void viewPropertyAnimator() { ViewPropertyAnimator animator = head.animate(); animator.translationX(200) .scaleX(2) .scaleY(2) .setDuration(2000) .start(); }
private void animationListener() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f); objectAnimator.setDuration(2000); objectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { Log.d("dingfeng", "onAnimationStart......"); } @Override public void onAnimationEnd(Animator animation) { Log.d("dingfeng", "onAnimationEnd......"); } @Override public void onAnimationCancel(Animator animation) { Log.d("dingfeng", "onAnimationCancel......"); } @Override public void onAnimationRepeat(Animator animation) { Log.d("dingfeng", "onAnimationRepeat......"); } }); objectAnimator.start(); }
可以根据不同需求来实现接口里面的四个方法,可以根据需要做相应的处理。但有时候你会觉得我不需要监听动画的四种状态,我只需要监听动画结束时候的状态,使用上面的方法就会感觉代码臃肿了,不过没关系,Android系统给我们提供了一个更好用的方法,
private void animationListener2() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f); objectAnimator.setDuration(2000); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); Log.d("dingfeng", "onAnimationEnd......"); } }); objectAnimator.start(); }
可以看出,我们使用了AnimatorListenerAdapter动画接口适配器代替AnimatorListener接口。其实AnimatorListenerAdapter的源码只是一个实现了AnimatorListener接口的抽象类而已,
你需要监听哪种动画状态就重写哪种方法就可以了。
除此之外,AnimatorUpdateListener接口就可以读取到动画的每个更新值。
private void animationListener3() { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(head, "translationX", 0.0f, 350.0f, 0f); objectAnimator.setDuration(2000); objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float)animation.getAnimatedValue(); Log.d("dingfeng", "onAnimationUpdate......"+value); } }); objectAnimator.start(); }一般情况下不太用到这个接口,但是在自定义动画的时候,通过该接口,可以实现很多复杂的效果。
我们创建两个动画文件,一个是旋转动画,一个组合动画
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android='http://schemas.android.com/apk/res/android' android:duration='2000' android:propertyName='rotationX' android:repeatCount="-1" android:repeatMode="restart" android:valueFrom='0' android:valueTo='180' android:valueType='floatType' />
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android='http://schemas.android.com/apk/res/android' android:duration='2000' android:ordering='sequentially'><!--动画执行顺序 sequentially:顺序执行;together:同时执行。 --> <objectAnimator android:propertyName='translationX' android:valueFrom='0' android:valueTo='200' android:valueType='floatType' /> <set android:ordering='together'> <objectAnimator android:propertyName='scaleX' android:valueFrom='1' android:valueTo='2' android:valueType='floatType' /> <objectAnimator android:propertyName='scaleY' android:valueFrom='1' android:valueTo='2' android:valueType='floatType' /> </set> </set>
head = (ImageView) findViewById(R.id.head); Animator animator = AnimatorInflater.loadAnimator(this, R.animator.multi); animator.setTarget(head); animator.start();这样就OK了,还是很容易的。