Android属性动画之ObjectAnimator和AnimatorSet

从上一篇文章Android属性动画之ValueAnimator我们知道ValueAnimator是监听动画过程,自己实现属性的改变,但是ObjectAnimator就有所不同,它真正可以作用在一个对象上,并且明确的指定了要更改的属性,属性的变化过程是它帮我们完成的,不需要我们自己来实现它的改变。

我们直接来举个例子,我们希望一幅图片可以水平移动。

ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "translationX", 0.0f, 350.0f, 0f);
animator.setDuration(2500).start();

可以看到在上面已经明确指定了属性名称translationX,这样整个改变过程内部已经帮我们完成,也就是说在ValueAnimator中我们只能获取到变化的值,属性值的更改过程由我们自己来完成,而在ObjectAnimator中,它帮我们做了两件事,首先获取到变化值,然后通过上面指定的属性名称来进行属性值的修改。

获取到ObjectAnimator对象的方法有以下几种:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
            float... values)

public static ObjectAnimator ofInt(Object target, String propertyName, int... values)

public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values)

public static ObjectAnimator ofObject(Object target, String propertyName,
            TypeEvaluator evaluator, Object... values)

public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
            TypeEvaluator<V> evaluator, V... values)

public static ObjectAnimator ofPropertyValuesHolder(Object target,
            PropertyValuesHolder... values)

跟前面一样,它们的不同点就是它们的变化值类型不同。

同样它也有许多设置设置值:

public void setTarget(Object target)
设置目标对象

public ObjectAnimator setDuration(long duration)
设置持续时间

public void setProperty(Property property)
设置属性

public void setPropertyName(String propertyName)
设置属性名称

public void setIntValues(int... values)
设置Int值,对应ObjectAnimator.ofInt

public void setFloatValues(float... values)
设置Float,对应ObjectAnimator.ofFloat

public void setObjectValues(Object... values)
设置Object值,对应ObjectAnimator.ofObject

另外,它继承自ValueAnimator,所以它也继承了ValueAnimator的设置函数。

下面举个例子:

ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.3f, 1.0F);
animator.setDuration(2000);//动画时间
animator.setInterpolator(new BounceInterpolator());//动画插值
animator.setRepeatCount(-1);//设置动画重复次数
animator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式
animator.setStartDelay(1000);//动画延时执行
animator.start();//启动动画

同样,也可以为这个动画添加事件监听,这个跟ValueAnimator相同。具体可以看看上一篇文章:Android属性动画之ValueAnimator

到了这里,我们需要来说说属性动画的原理了。
从上面我们看到ObjectAnimator需要指定变化的属性名,当它获取到变化值后,就会通过这个属性名的set方法来设置这个属性值。属性动画根据你传递的该属性的初始值和最终值,动态的去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。所以,所以必须为对象对应的属性提供get、set方法。

下面总结一下:
1. object必须要提供setXxx方法,如果动画的时候没有传递初始值,那么还要提供getXxx方法,因为系统要去拿xxx属性的初始值。

2、另外设置属性值之后,应该使用requestLayout()或者invalidate()去进行刷新操作,这样才能看到动画效果。

当我们需要对某个对象的属性进行动画,但是这个属性没有提供对应的getXxx()、setXxx()方法该怎么办,我们有三种方法可以做:
1. 给你的对象加上get和set方法,如果你有权限的话
2. 用一个类来包装原始对象,间接为其提供get和set方法
3. 采用ValueAnimator,监听动画过程,自己实现属性的改变

另外,我们我们看到每一种动画属性都有两种方式一种是指定属性名,一种是指定一个Property对象,它对应的就是我们的自定义属性。假如我们希望更改我们自定义的某个属性,我们就可以使用这个,原理是相同的,就是不断的修改对应的属性值,下面来举个例子,假如我们自定义了一个圆,我们希望这个圆可以做一个动画就是半径可以指定的范围内进行变化。

自定义圆CircleAnim,它有三个属性:中心点坐标和变径

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleAnim">
        <attr name="pivotX" format="dimension"/>
        <attr name="pivotY" format="dimension"/>
        <attr name="radius" format="dimension"/>
    </declare-styleable>

</resources>
public class CircleAnim  extends View {
    private float mPivotX;
    private float mPivotY;
    private float mRadius;
    private Paint mPaint;

    public CircleAnim(Context context) {
        this(context, null);
    }

    public CircleAnim(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleAnim(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleAnim);
        mPivotX = ta.getDimension(R.styleable.CircleAnim_pivotX, 0);
        mPivotY = ta.getDimension(R.styleable.CircleAnim_pivotY, 0);
        mRadius = ta.getDimension(R.styleable.CircleAnim_radius, 0);
        ta.recycle();

        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.parseColor("#0F000F"));
    }

    public void setRadius(float radius) {
        mRadius = radius;
        invalidate();
    }

    public float getRadius() {
        return mRadius;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(mPivotX, mPivotY, mRadius, mPaint);
        super.onDraw(canvas);
    }
}

假如我们希望这个圆实现一个动画,就是变径在指定的范围内改变。

public class MainActivity extends AppCompatActivity {
    private CircleAnim mCircleAnim;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mCircleAnim = (CircleAnim) findViewById(R.id.circle);
        final ObjectAnimator animator = ObjectAnimator.ofFloat(mCircleAnim, new RadiusProperty(CircleAnim.class, "CircleAnim"), 0, dp2px(40));
        animator.setDuration(5000);


        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animator.start();
            }
        });
    }

    private float dp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }
}

可以看到上面的核心代码为:

ObjectAnimator.ofFloat(mCircleAnim, new RadiusProperty(CircleAnim.class, "CircleAnim"), 0, dp2px(40));
        animator.setDuration(5000);

可以看到我们指定变化属性为RadiusProperty,这个是我们自定义的属性,实现如下:

class RadiusProperty extends Property<CircleAnim, Float> {
        public RadiusProperty(Class type, String name) {
            super(type, name);
        }

        @Override
        public Float get(CircleAnim object) {
            return object.getRadius();
        }

        @Override
        public void set(CircleAnim object, Float value) {
            object.setRadius(value);
        }
    }

在动画的执行过程中会不断的去通过调用set函数去设置变化的变径,在这个里面我们调用的就是CircleAnim的setRadius函数来设置我们圆的变径,在setRadius里面得到这个变径之后,调用invalidate函数,这样进行重绘就会画出指定变径的圆,由于整个过程是连续的,所以就实现了一个半径不断变化的动画效果。

搞清楚了原理,就该说说组合动画了,组合动画有以下几种方式:
1、使用AnimatorSet
使用AnimatorSet来进行组合动画也有两种处理方式。
(1)使用AnimatorSet的play()方法
这个类有一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

public Builder with(Animator anim)
将现有动画和传入的动画同时执行

public Builder before(Animator anim)
将现有动画插入到传入的动画之前执行

public Builder after(Animator anim)
将现有动画插入到传入的动画之后执行

public Builder after(long delay)
将现有动画延迟指定毫秒后执行

下面来举个例子:

ObjectAnimator animator = ObjectAnimator.ofInt(container, "backgroundColor", 0xFFFF0000, 0xFFFF00FF);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "translationX", 0.0f, 200.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "scaleX", 1.0f, 2.0f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(imageView, "rotationX", 0.0f, 90.0f, 0.0F);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0.2f, 1.0F);

//组合动画方式1
AnimatorSet set = new AnimatorSet();
((set.play(animator).with(animator1).before(animator2)).before(animator3)).after(animator4);
set.setDuration(5000);
set.start();

(2)使用AnimatorSet类中的playTogether(Animator… items)方法实现多个动画同时执行的效果

2、使用PropertyValuesHolder
在ObjectAnimator.ofPropertyValuesHolder(Object target,
PropertyValuesHolder… values)方法里面,它的第三个参数接收一个PropertyValuesHolder参数,这个参数代表一个属性值集合,我们可以传入多个属性及其变化值范围,让这些属性同时变化,从而就实现了组合动画。

PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("translationX", 0.0f, 300.0f);
PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f, 0.0F);
PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.3f, 1.0F);

ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, valuesHolder, valuesHolder1, valuesHolder2, valuesHolder3);
objectAnimator.setDuration(2000).start();

从上面这个例子中就可以很容易的看出它的用法了。下面我们来看看PropertyValuesHolder,它提供了四种类型的值Int,Float,Object,Keyframe。

public static PropertyValuesHolder ofInt(String propertyName, int... values)

public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values)

public static PropertyValuesHolder ofFloat(String propertyName, float... values)

public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values)

public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
            Object... values)

public static <V> PropertyValuesHolder ofObject(Property property,
            TypeEvaluator<V> evaluator, V... values)

public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)

public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values)

我们主要说说Keyframe。
一个 keyframe 对象由一对 time / value 的键值对组成,可以为动画定义某一特定时间的特定状态,Animator 传入的一个个参数映射为一个个 keyframe,存储相应的动画的触发时间和属性值;

也就是说我们可以使用Keyframe来为某个时间点指定一个变化值,在前面。。。我们说过,属性动画是使用时间变化的百分比来计算属性值的变化的,我们为动画设置过持续时间Duration,这样通过Keyframe我们就可以在这个持续时间的某个时间点来设置一个具体的变化值,当到达这个时间点的时候,就会为对应的属性设置这个变化值。

另外我们需要注意的是上面所说的那个时间点,它是一个float值,这个值对应的就是插值器getInterpolation函数返回的那个值,对应的变化值类型有以下几种:

public static Keyframe ofInt(float fraction, int value)

public static Keyframe ofInt(float fraction)

public static Keyframe ofFloat(float fraction, float value)

public static Keyframe ofFloat(float fraction)

public static Keyframe ofObject(float fraction, Object value)

public static Keyframe ofObject(float fraction)

上面基本就是用来指定time / value 的键值对,其中的time就是变化时间百分比,value就是变化值,它的意思就是time这个时间变化百分比对应的value变化值。

我们可以指定多个time / value 的键值对,也就是创建多个Keyframe对象,将它们传入上面的PropertyValuesHolder.ofKeyframe函数中,就相当于我们在多个时间变化百分比分别指定了不同的变化值,所以在不同的时间点,会为指定的属性设置对应的属性变化值,这样就是实现了动画。

下面我们来举个例子。

Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);
Keyframe keyframe2 = Keyframe.ofFloat(.5f, 200.0f);
Keyframe keyframe3 = Keyframe.ofFloat(1f, 0f);

PropertyValuesHolder property = PropertyValuesHolder.ofKeyframe("translationX", keyframe1, keyframe2, keyframe3);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, property);

3、使用ViewPropertyAnimator

下面我先来介绍几个函数:

public static ViewPropertyAnimator animate(View view)
指定需要动画的View,返回一个ViewPropertyAnimator对象

public abstract ViewPropertyAnimator setDuration(long duration);
设置持续时间

public abstract long getDuration();
得到持续时间

public abstract long getStartDelay();
得到开始延迟

public abstract ViewPropertyAnimator setStartDelay(long startDelay);
设置开始延迟

public abstract ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator);
设置插值器

public abstract ViewPropertyAnimator setListener(Animator.AnimatorListener listener);
设置监听器

public abstract void start();
开始动画

public abstract void cancel();
取消动画

public abstract ViewPropertyAnimator x(float value);
对属性x执行动画

public abstract ViewPropertyAnimator xBy(float value);
对属性x执行动画

public abstract ViewPropertyAnimator y(float value);
对属性y执行动画

public abstract ViewPropertyAnimator yBy(float value);
对属性y执行动画

public abstract ViewPropertyAnimator rotation(float value);
对属性rotation执行动画

public abstract ViewPropertyAnimator rotationBy(float value);
对属性rotation执行动画

public abstract ViewPropertyAnimator rotationX(float value);
对属性rotationX执行动画

public abstract ViewPropertyAnimator rotationXBy(float value);
对属性rotationX执行动画

public abstract ViewPropertyAnimator rotationY(float value);
对属性rotationY执行动画

public abstract ViewPropertyAnimator rotationYBy(float value);
对属性rotationY执行动画

public abstract ViewPropertyAnimator translationX(float value);
对属性translationX执行动画

public abstract ViewPropertyAnimator translationXBy(float value);
对属性translationX执行动画

public abstract ViewPropertyAnimator translationY(float value);
对属性translationY执行动画

public abstract ViewPropertyAnimator translationYBy(float value);
对属性translationY执行动画

public abstract ViewPropertyAnimator scaleX(float value);
对属性scaleX执行动画

public abstract ViewPropertyAnimator scaleXBy(float value);
对属性scaleX执行动画

public abstract ViewPropertyAnimator scaleY(float value);
对属性scaleY执行动画

public abstract ViewPropertyAnimator scaleYBy(float value);
对属性scaleY执行动画

public abstract ViewPropertyAnimator alpha(float value);
对属性alpha执行动画

public abstract ViewPropertyAnimator alphaBy(float value);
对属性alpha执行动画

弄清楚了上面的函数,我们来看个例子。

ViewPropertyAnimator animator5 = ViewPropertyAnimator.animate(imageView);
animator5.translationX(200).scaleX(2).setDuration(2000).start();

上面平移和X轴放大动画会同时执行。

最后需要说明的是,上面分析是基于NineOldAnimations属性动画来说的,它的用法跟系统提供的基本一样,只是它可以兼容低版本。

参考文章:
http://blog.csdn.net/singwhatiwanna/article/details/17841165

http://blog.csdn.net/feiduclear_up/article/details/45915377

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值