文章目录

  • 属性动画
  • ValueAnimator
  • ValueAnimator.ofInt
  • ValueAnimator.ofArgb:
  • Evaluator
  • ValueAnimator.ofObject
  • ValueAnimator.ofPropertyValuesHolder
  • ObjectAnimator
  • AnimatorSet
  • animatorSet.playTogether()
  • animatorSet.playSequentially()
  • animatorSet.play().with().before().after()


属性动画

由于补间动画的不足,Android又引入了属性动画,直接改变对象的属性实现动画效果

androidprogressbar打开控件动画 安卓控件动画_lua

补间动画和属性动画的区别:

命名的区别:

  • 补间动画是xxxxAnimation
  • 属性动画是xxxxAnimator

动画种类:

  • 补间动画只有平移、缩放、旋转、透明度动画
  • 属性动画可以对所有属性变化实现动画

动画状态:

  • 补间动画是伪动画,比如点击事件只能在原位置触发
  • 属性动画的当前状态,也是对象的真实状态

ValueAnimator

ValueAnimator.ofInt

我们简单的实现个动画例子:

animator1Tv=findViewById(R.id.animator1Tv);
ValueAnimator animator1=ValueAnimator.ofInt(0,100);
animator1.setDuration(2000);
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        animator1Tv.setTranslationY((int) animation.getAnimatedValue());
    }
});

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

androidprogressbar打开控件动画 安卓控件动画_android_02

我们使用:

ValueAnimator animator1=ValueAnimator.ofInt(0,100)

实现了一个从0到100的数值变化过程,再将变化过程中的值设置给textview.setTranslationY()改变textview的位置实现动画效果。

可以看出,ValueAnimator本身并不是对View操作,而是对值操作,是值之间的变化过程

除了ValueAnimator.ofInt()直接构建ValueAnimator对象外,我们还有:

  • ValueAnimator.ofFloat()
  • ValueAnimator.ofArgb()
  • ValueAnimator.ofObject()
  • ValueAnimator.ofPropertyValuesHolder()

除了直接使用上面的静态方法构建ValueAnimator对象外,我们也可以直接new对象实现,比如上面的ofInt()可以写成:

ValueAnimator va=new ValueAnimator();
va.setIntValue(0,100)

他们之间的关系我整理如下图:

androidprogressbar打开控件动画 安卓控件动画_Math_03

几个方法之间是有关系的,比如,我们定义一个动画可以有这四种不同的方式:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    animator1Tv=findViewById(R.id.animator1Tv);
    animator2Tv=findViewById(R.id.animator2Tv);
    animator3Tv=findViewById(R.id.animator3Tv);
    animator4Tv=findViewById(R.id.animator4Tv);

    final ValueAnimator animator1=ValueAnimator.ofInt(0,100);

    final ValueAnimator animator2=new ValueAnimator();
    animator2.setIntValues(0,100);

    final ValueAnimator animator3=ValueAnimator.ofObject(new IntEvaluator(),0,100);

    final ValueAnimator animator4=ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder.ofInt("",0,100));

    animator1.addUpdateListener(new AnimatorListener(0));
    animator2.addUpdateListener(new AnimatorListener(1));
    animator3.addUpdateListener(new AnimatorListener(2));
    animator4.addUpdateListener(new AnimatorListener(3));

    animator1.setDuration(2000);
    animator2.setDuration(2000);
    animator3.setDuration(2000);
    animator4.setDuration(2000);

    findViewById(R.id.startTv).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            animator1.start();
            animator2.start();
            animator3.start();
            animator4.start();
        }
    });
}

public class AnimatorListener implements ValueAnimator.AnimatorUpdateListener{

    private int index;
    public AnimatorListener(int index){
        this.index=index;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int value= (int) animation.getAnimatedValue();
        switch (index){
            case 0:
                animator1Tv.setTranslationY(value);
                break;
            case 1:
                animator2Tv.setTranslationY(value);
                break;
            case 2:
                animator3Tv.setTranslationY(value);
                break;
            case 3:
                animator4Tv.setTranslationY(value);
                break;
        }
    }
}

androidprogressbar打开控件动画 安卓控件动画_lua_04

可以看到,四种方式构建的动画效果是一样的。

ValueAnimator.ofInt()是对int值变化

ValueAnimator.ofFloat()是对float值变化

ValueAnimator.ofArgb() 对颜色的变化

ValueAnimator.ofArgb:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);

    final ValueAnimator animator = ValueAnimator.ofArgb(0xffff0000, 0xff0000ff);

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            textView.setBackgroundColor((int) animation.getAnimatedValue());
        }
    });

    animator.setDuration(5000);

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

}

androidprogressbar打开控件动画 安卓控件动画_lua_05

TextView的背景色从0xffff0000(红色)变化成0xff0000ff(蓝色),我们再看看ofArgb()方法:

public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    return anim;
}

可以看到,ofArgb()也是调用的va.setIntValues()方法,那它是如何实现颜色的渐变呢?ofInt()纯数值我们还可以蒙一下是数值递增什么的,这个颜色值的变化规则是什么?

Evaluator

ArgbEvaluator是如何实现颜色的渐变效果的?

public class ArgbEvaluator implements TypeEvaluator {
    private static final ArgbEvaluator sInstance = new ArgbEvaluator();

    public static ArgbEvaluator getInstance() {
        return sInstance;
    }
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        //将startValue(0xffff0000)十六进制转成int
        //int整形有32位,0xffff0000(11111111 11111111 00000000 00000000)
        int startInt = (Integer) startValue;
        //右移24位,(00000000 00000000 00000000 11111111)
        //& 0xff 后得:(00000000 00000000 00000000 11111111)
        //&操作符,同位都是1,即为1
        //取出Alpha通道的值
        float startA = ((startInt >> 24) & 0xff) / 255.0f;
        //取出R通道的值
        float startR = ((startInt >> 16) & 0xff) / 255.0f;
        //取出G通道的值
        float startG = ((startInt >>  8) & 0xff) / 255.0f;
        //取出B通道的值
        float startB = ( startInt        & 0xff) / 255.0f;

        int endInt = (Integer) endValue;
        float endA = ((endInt >> 24) & 0xff) / 255.0f;
        float endR = ((endInt >> 16) & 0xff) / 255.0f;
        float endG = ((endInt >>  8) & 0xff) / 255.0f;
        float endB = ( endInt        & 0xff) / 255.0f;

        // convert from sRGB to linear
        startR = (float) Math.pow(startR, 2.2);
        startG = (float) Math.pow(startG, 2.2);
        startB = (float) Math.pow(startB, 2.2);

        endR = (float) Math.pow(endR, 2.2);
        endG = (float) Math.pow(endG, 2.2);
        endB = (float) Math.pow(endB, 2.2);

        //基于fraction(Intepolator中的t)动画当前进度计算出新的argb
        float a = startA + fraction * (endA - startA);
        float r = startR + fraction * (endR - startR);
        float g = startG + fraction * (endG - startG);
        float b = startB + fraction * (endB - startB);

        // convert back to sRGB in the [0..255] range
        a = a * 255.0f;
        r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
        g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
        b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

        //对计算出来的argb进行位操作放到对应的位置合成新的int颜色值
        return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
    }
}

可以看到ArgbEvaluator只实现了一个方法evaluate()并根据fraction当前进度计算出新的颜色值,这就是颜色变化的计算规则了。

既然Evaluator和Interpolator都可以对动画效果进行控制,那么他们的区别是啥?

在LinearInterpolator中,getInterpolation返回的是View在路程中的比例,比如返回0.5f,那就是表示view运动了一半:

//LinearInterpolator.java
public float getInterpolation(float input) {
    return input;
}

那它这个比例是如何换算成具体数值给view使用的呢?

我们拿TranslateAnimation平移动画的实现来看看:

//TranslateAnimation.java
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float dx = mFromXDelta;
    float dy = mFromYDelta;
    if (mFromXDelta != mToXDelta) {
        dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
    }
    if (mFromYDelta != mToYDelta) {
        dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
    }
    t.getMatrix().setTranslate(dx, dy);
}

参数interpolatedTime就是getInterpolation返回值(路程比例)

dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
 dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);

用总路程(end-start)*interpolatedTime(路程比例),就可以得出当前走了多少路程,再加上start就知道从start开始算,走了多远的距离。这就是最终运用给view的数值。

我们再来看看,在ValueAnimator中又是如何处理Interpolator的具体数值转化的

//ValueAnimator.java
@CallSuper
@UnsupportedAppUsage
void animateValue(float fraction) {
    //根据当前时间进度fraction[0,1],求得路程进度fraction[可大于1,也可小于0,即view可超出终点和起点]
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    //mValues为封装的ProperyValuesHolder数组,封装了如ofInt(0,100)两个值
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        //根据封装的每个参数[0,100]和进度fraction,计算出具体值
        mValues[i].calculateValue(fraction);
    }
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            //最终通过我们设置的动画监听器用animation.getAnimatedValue()获取值给view使用
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}
//PropertyValuesHolder.java
void calculateValue(float fraction) {
    Object value = mKeyframes.getValue(fraction);
    mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
//FloatKeyframeSet.java

@Override
public Object getValue(float fraction) {
    return getFloatValue(fraction);
}

@Override
public float getFloatValue(float fraction) {
    //当位置进度小于起点位置
    if (fraction <= 0f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    //当位置进度大于终点位置                    
    } else if (fraction >= 1f) {
        final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
        final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
        float prevValue = prevKeyframe.getFloatValue();
        float nextValue = nextKeyframe.getFloatValue();
        float prevFraction = prevKeyframe.getFraction();
        float nextFraction = nextKeyframe.getFraction();
        final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
        if (interpolator != null) {
            fraction = interpolator.getInterpolation(fraction);
        }
        float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
        return mEvaluator == null ?
                prevValue + intervalFraction * (nextValue - prevValue) :
                ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
    }
    //当位置进度在[0,1]之间
    FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
    //遍历每一关键帧
    for (int i = 1; i < mNumKeyframes; ++i) {
        FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
        if (fraction < nextKeyframe.getFraction()) {
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            //算出当前帧在前一帧和后一帧之间的比例
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());
            float prevValue = prevKeyframe.getFloatValue();
            float nextValue = nextKeyframe.getFloatValue();
            // Apply interpolator on the proportional duration.
            //根据当前进度,换算出我们设置的Interpolator反应出的新的进度比例。
            if (interpolator != null) {
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            //如果设置了Interpolator,最终会影响值得计算
            return mEvaluator == null ?
                    prevValue + intervalFraction * (nextValue - prevValue) :
                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't get here
    return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}

我们可以看到最终的一个算法

return mEvaluator == null ?prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).floatValue();
  • 当Evaluator==null时,跟我们前面TranslateAnimation的算法一致
  • 当Evaluator!=null时,直接调用了Evaluator.evaluate()计算具体数值

总结:

Interpolator返回的计算值是动画当前位置所在总路程的比例,帧动画(如TranslateAnimation)中,实现了根据这个比例算出具体数值应用给view实现动画
Evaluator返回的计算值就是根据动画当前位置所在总路程的比例,算出的具体数值给view实现动画(也就是TranslateAnimation中换算具体数值的操作)

也就是说,在帧动画中,已经帮我们实现了具体值的换算,而在ValueAnimator中,需要我们自己实现Evaluator来实现值的换算

有朋友会有疑问了,在ofInt(),ofFloat()中,我们并没有设置Evaluator来换算值啊?经过上面的ofArgb(),我们知道,内部帮我们设置了一个ArgbEvaluator,那ofInt(),ofFloat()肯定也是内容帮我们设置好了对应的Evaluator了。

//PropertyValuesHolder.java
void init() {
    if (mEvaluator == null) {
        // We already handle int and float automatically, but not their Object
        // equivalents
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
    if (mEvaluator != null) {
        // KeyframeSet knows how to evaluate the common types - only give it a custom
        // evaluator if one has been set on this class
        mKeyframes.setEvaluator(mEvaluator);
    }
}

查看源码,底层确实帮我们设置好了对应Evaluator了。

private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
public class IntEvaluator implements TypeEvaluator<Integer> {

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

public class FloatEvaluator implements TypeEvaluator<Number> {

    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

可以看到

startInt + fraction * (endValue - startInt)

是一种常用的值换算算法。

ofInt(),ofFloat(),ofArgb()都帮我们实现了Evaluator来实现值的换算,那ofObject()该怎么办?当然要我们自己自定义Evaluator来实现啦。

ValueAnimator.ofObject

我们举个例子,有一只猫,出生时有10根毛发,成年时有1000根毛发,求这只猫从出生到成年毛发增长的数值变化过程。

我们定义一个类Cat.class:

public class Cat{
    //当前猫的状态(0:出生,1:成年)
    private String status;
    //毛发的数量
    private int hair;

    public Cat(String status, int hair) {
        this.status = status;
        this.hair = hair;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public int getHair() {
        return hair;
    }

    public void setHair(int hair) {
        this.hair = hair;
    }
}

然后我们使用ValueAnimator.ofObject()设置两个状态的猫,并通过AccelerateInterpolator实现加速的一种变化效果

ValueAnimator animator = ValueAnimator.ofObject(new MyEvaluator(),new Cat("幼崽",10),new Cat("成年",1000));
animator.setInterpolator(new AccelerateInterpolator());

接着,我们自定义MyEvaluator实现具体毛发数量值的计算:

public class MyEvaluator implements TypeEvaluator<Cat> {

    @Override
    public Cat evaluate(float fraction, Cat startCat, Cat endCat) {
        return new Cat("长毛中", (int) (startCat.getHair()+(endCat.getHair()-startCat.getHair())*fraction));
    }
}

所有代码:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);

    final ValueAnimator animator = ValueAnimator.ofObject(new MyEvaluator(),new Cat("幼崽",10),new Cat("成年",1000));
    animator.setInterpolator(new AccelerateInterpolator());

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Cat cat= (Cat) animation.getAnimatedValue();
            textView.setText(cat.getStatus()+":"+cat.getHair());
        }
    });
    animator.setDuration(5000);


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

public class Cat{
    //当前猫的状态
    private String status;
    //毛发的数量
    private int hair;

    public Cat(String status, int hair) {
        this.status = status;
        this.hair = hair;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public int getHair() {
        return hair;
    }

    public void setHair(int hair) {
        this.hair = hair;
    }
}

public class MyEvaluator implements TypeEvaluator<Cat> {

    @Override
    public Cat evaluate(float fraction, Cat startCat, Cat endCat) {
        return new Cat("长毛中", (int) (startCat.getHair()+(endCat.getHair()-startCat.getHair())*fraction));
    }
}

androidprogressbar打开控件动画 安卓控件动画_android_06

ValueAnimator.ofPropertyValuesHolder

根据一开始我们给出的思维导图我们知道,ofInt()…ofPropertyValuesHoler()最终都是调用的setValues()方法

//ValueAnimator.java
public void setValues(PropertyValuesHolder... values) {
    int numValues = values.length;
    mValues = values;
    mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
    for (int i = 0; i < numValues; ++i) {
        PropertyValuesHolder valuesHolder = values[i];
        mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

比如ofInt()的setIntValues():

//ValueAnimator.java
public void setIntValues(int... values) {
    if (values == null || values.length == 0) {
        return;
    }
    if (mValues == null || mValues.length == 0) {
        setValues(PropertyValuesHolder.ofInt("", values));
    } else {
        PropertyValuesHolder valuesHolder = mValues[0];
        valuesHolder.setIntValues(values);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

所以我们的ofInt还可以这样实现:

final ValueAnimator animator = ValueAnimator.ofInt(0,100);
final ValueAnimator animator=ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder.ofInt("",0,100));

PropertyValuesHolder

根据名字我们分析,这个东西保存了一个属性名(可以为空字符串)以及属性值values,我们猜测,这个对象实现了值values的变化过程并把变化的值设置给了这个名称的属性,从而实现了属性动画

我们来看看ProeryValuesHolder提供了哪些构造方法:

androidprogressbar打开控件动画 安卓控件动画_ide_07

那我们猜测,可以通过ofInt(“Rotation”,0,100)实现对View旋转100度的动画

看个例子:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);

    final ValueAnimator animator=ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder.ofFloat("Rotation",0,100));
    animator.setTarget(textView);

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
           int value= (int) animation.getAnimatedValue();
            textView.setText(value+"");
        }
    });
    animator.setDuration(5000);


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

androidprogressbar打开控件动画 安卓控件动画_android_08

通过AnimatorUpdateListener我们知道,值确实变化了,但并没有旋转,难道是 animator.setTarget(textView);没起作用,导致值没有绑定到target的对应属性上?

//Animator.java
public void setTarget(@Nullable Object target) {
}

通过源码,我们知道这个是抽象类Animator的方法,然而我们在它的实现类ValueAnimator中并没有发现重写了这个方法,也就是说,ValueAnimator不能设置Target并绑定属性,进一步明确,ValueAnimator只是对值的一个操作。

既然ValueAnimator不行,那我们换它的继承类ObjectAnimator试试:

ObjectAnimator

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);


   final ValueAnimator animator= ObjectAnimator.ofPropertyValuesHolder(textView,PropertyValuesHolder.ofFloat("Rotation",0,100));

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
           float value= (float) animation.getAnimatedValue();
            textView.setText(value+"");
        }
    });
    animator.setDuration(5000);


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

androidprogressbar打开控件动画 安卓控件动画_lua_09

所以,要绑定属性进行动画:

  • 要么用ValueAnimator,在onAnimationUpdate回调函数中自己根据变化的值设置给view的属性实现动画
  • 要么用ObjectAnimator,设置对象和对应属性,即可自动实现改变对应属性实现动画

ObjectAnimator继承至ValueAnimator,所以用法跟ValueAnimator是一样的,不同的是多了对象和属性的绑定,绑定属性的前提是对象中必须要有对应的set方法,比如上面的属性"Rotation"能起作用,是因为TextView中有setRotation(float rotation)方法,注意参数类型是float,所以要用ofFloat(),用ofInt()是不行的。

用法没啥难度,就不细讲了,最后就是补一下关键帧的一个用法:

PropertyValuesHolder.ofKeyframe("Rotation", Keyframe.ofFloat(0,0),Keyframe.ofFloat(0.5f,90),Keyframe.ofFloat(1f,0))

意思是,我定义三个关键帧:

  • 第一个关键帧为进度为0时,角度为0
  • 第一个关键帧为进度为0.5时,角度为90
  • 第一个关键帧为进度为1时,角度为0

也就是定义了一个从0旋转到90度再旋转到0度的动画

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);


   final ValueAnimator animator= ObjectAnimator.ofPropertyValuesHolder(textView,PropertyValuesHolder
           .ofKeyframe("Rotation", Keyframe.ofFloat(0,0),Keyframe.ofFloat(0.5f,90),Keyframe.ofFloat(1f,0)));
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
           float value= (float) animation.getAnimatedValue();
            textView.setText(value+"");
        }
    });
    animator.setDuration(5000);


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

androidprogressbar打开控件动画 安卓控件动画_android_10

AnimatorSet

AnimatorSet就是几个动画的集合,用的也比较多,我们了解一下

animatorSet.playTogether()

几个动画同时进行

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_value_animator);

    textView = findViewById(R.id.textView);

    ObjectAnimator rotation = ObjectAnimator.ofFloat(textView, "Rotation", 0, 360);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(textView, "ScaleX", 1, 2, 1);
    ObjectAnimator translationX = ObjectAnimator.ofFloat(textView, "TranslationX", 0, 100, 0, -100, 0);

    final AnimatorSet animatorSet=new AnimatorSet();
    animatorSet.setDuration(3000);
    animatorSet.playTogether(rotation,scaleX,translationX);

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

androidprogressbar打开控件动画 安卓控件动画_Math_11

animatorSet.playSequentially()

几个动画按序进行

androidprogressbar打开控件动画 安卓控件动画_Math_12

那如果我有的想一起进行,有的想按序进行怎么办呢?

animatorSet.play().with().before().after()

按需组合顺序

animatorSet.play(rotation).with(scaleX).before(translationX);

androidprogressbar打开控件动画 安卓控件动画_Math_13