前言
Android3.0之前系统只提供了补间动画,但是它只能针对View做动画而且只能在改变展示状态无法整整的改变View的属性值。比如补间动画的偏移效果虽然View的位置在外观上改变了,但实际的点击效果还需要在之前的位置点击才会回调。3.0之后提供的属性动画不但能够对View做动画还能够对数值做动画效果,所谓属性动画其实就是对具有get和set方法的属性值做动画效果,这样不但是View的外观发生变化还使得它的相关属性值真正发生改变。
ValueAnimaor对象
ValueAnimator正如它的命名一样它是针对值做动画效果,比如Java中有常见的Integer/Float等数值,可以实现一个数值不停变化的动画效果。
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueType="intType"
android:interpolator="@android:anim/accelerate_interpolator"
android:duration="2000"
android:valueFrom="0"
android:valueTo="1000">
</animator>
在代码中也可以直接定义属性动画,不过在XML中定义的便于复用,两种写法是等价的。
valueAnimation = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value);
valueAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText(String.valueOf(animation.getAnimatedValue()));
}
});
valueAnimation.start();
开始动画之后数值会一致不停的增长直到最大值,效果如下:
ValueAnimator是所有属性动画的最基础实现,它只能对数值类型的对象做动画,如果需要对View等对象做属性动画可以使用它的子类ObjectAnimator来简单实现。
ObjectAnimator对象
查看ObjectAnimator的所有工厂方法接口主要有以下几个初始化对象属性动画接口有很多中,这里主要挑选几个常用的属性动画接口来试用。
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
public static <T, V> ObjectAnimator ofObject(T target, @NonNull Property<T, V> property,
@Nullable TypeConverter<PointF, V> converter, Path path)
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values)
ofFloat属性动画
补间动画的内置四种动画效果都可以使用这个属性动画来实现,因为View自带了setAlpha/setRotate/setRotateX/setRotateY/setTranslationX/setTranslationY/scaleX/scaleY等多个属性get/set方法。
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueType="floatType"
android:propertyName="alpha"
android:duration="1000"
android:valueFrom="1"
android:valueTo="0.3">
</objectAnimator>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="360"
android:propertyName="rotation"
android:valueType="floatType"
android:duration="100"
android:repeatCount="1">
</objectAnimator>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="250"
android:propertyName="translationX"
android:valueType="floatType"
android:duration="1000">
</objectAnimator>
这几个动画就实现了内置的补间动画效果。
这些动画效果很容易实现是因为View自带了这些属性值,对于那些View不会自动维护的属性值比如width需要使用包装对象来实现属性动画效果。
// 包装对象包含了width属性,也就是实现了setWidth和getWidth
private static class ViewWrapper {
private View view;
public ViewWrapper(View view) {
this.view = view;
}
public void setWidth(int width) {
view.getLayoutParams().width = width;
view.requestLayout();
}
public int getWidth() {
return view.getLayoutParams().width;
}
}
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="350"
android:valueTo="750"
android:propertyName="width"
android:valueType="intType"
android:duration="1000">
</objectAnimator>
widthAnimation = AnimatorInflater.loadAnimator(this, R.animator.width);
widthAnimation.setTarget(new ViewWrapper(button));
widthAnimation.start();
ofArgb动画
这个动画是专门针对颜色类型的值做动画效果,比如常见的背景颜色动画,执行过程中View的背景会随着时间流逝逐渐从一种颜色变换成另外一种新的颜色。
ObjectAnimator animator = ObjectAnimator.ofInt(button, "backgroundColor", Color.LTGRAY, Color.CYAN);
animator.setEvaluator(new ArgbEvaluator());
animator.start();
ofObject属性动画
这个Object指的是属性是Object类型,比如前面的setWidth这个属性的类型是Integer类型。View对象在展示的时候都有一个位置属性,可以使用left和top属性来标志,可以使用一个Position对象来保存这两个属性值。
private static class Position {
public float x;
public float y;
public Position(float x, float y) {
this.x = x;
this.y = y;
}
}
但是在View里并没有setPosition和getPosition这个属性的定义,直接继承View来添加这两个方法也不是一个好办法,假如还需要为ImageView和TextView也添加这个属性,那么就需要再定义两个子类分别继承ImageView和TextView。属性动画提供了一Property这个接口来实现外加的属性。
private static class PositionProperty extends Property<View, Position> {
public PositionProperty(Class<Position> type, String name) {
super(type, name);
}
@Override
public Position get(View view) {
return new Position(view.getX(), view.getY());
}
@Override
public void set(View view, Position value) {
view.setX(value.x);
view.setY(value.y);
}
}
这个接口的第一个泛型就是需要增加属性的类,第二个参数则是添加的属性类型,需要用户实现get/set方法来实现属性的绑定操作。
ObjectAnimator animator = ObjectAnimator.ofObject(image, new PositionProperty(Position.class, "position"), new PositionEvaluator(),
new Position(100, 100), new Position(500, 200), new Position(300, 500));
animator.setDuration(3000);
animator.start();
ofObject接口添加了ImageView的position属性动画,由于属性类型是Object类型需要用户自己来做对象的值解析操作。
private static class PositionEvaluator implements TypeEvaluator<Position> {
@Override
public Position evaluate(float fraction, Position startValue, Position endValue) {
// 计算Position随着时间流逝的属性值变化
float x = (endValue.x - startValue.x) * fraction + startValue.x;
float y = (endValue.y - startValue.y) * fraction + startValue.y;
return new Position(x, y);
}
}
最终的实现效果如下:
ofPropertyValuesHolder动画
属性值保持对象从命名上可以知道它是一个保存属性的多个值的容器,属性动画其实是一个过程用户定义了它的开始和结束值中间的值都有属性动画框架来自动生成,所有这些值对应的中间状态都会存放到PropertyValueHolder当中,如果属性动画需要同时做几个属性的动画效果,可以将每个属性都定义到一个PropertyValueHolder里然后将它们统一放到ObjectAnimator对象里,这样就实现了同时做多个属性动画的功能。
PropertyValuesHolder translationX = PropertyValuesHolder.ofFloat("translationX", 0, 200);
PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", 0, 200);
PropertyValuesHolder rotationX = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
PropertyValuesHolder rotationY = PropertyValuesHolder.ofFloat("rotationY", 0, 360);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(button, translationX, translationY, rotationX, rotationY);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.start();
KeyFrame也就是通常所说的关键帧,实际上动画执行时只是生成关键的帧每个关键帧之间的帧都是计算机临时生成的,PropertyValuesHolder里存放的中间状态就是这些关键帧,用户也可以自定义关键帧将它们存储在PropertyValuesHolder里,实现的属性动画就会在这些关键帧中不停的做动画。
Keyframe keyframe = Keyframe.ofFloat(0f, 0f);
Keyframe keyframe1 = Keyframe.ofFloat(0.1f, -60f);
Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 60f);
Keyframe keyframe3 = Keyframe.ofFloat(0.3f, -60f);
Keyframe keyframe4 = Keyframe.ofFloat(0.4f, 60f);
Keyframe keyframe5 = Keyframe.ofFloat(0.5f, -60f);
Keyframe keyframe6 = Keyframe.ofFloat(0.6f, 60f);
Keyframe keyframe7 = Keyframe.ofFloat(0.7f, -60f);
Keyframe keyframe8 = Keyframe.ofFloat(0.8f, 60f);
Keyframe keyframe9 = Keyframe.ofFloat(0.9f, -60f);
Keyframe keyframe10 = Keyframe.ofFloat(1.0f, 0f);
PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotationX",
keyframe, keyframe1, keyframe2, keyframe3, keyframe4,
keyframe5, keyframe6, keyframe7, keyframe8, keyframe9, keyframe10);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(image, propertyValuesHolder);
animator.setDuration(5000);
animator.start();