Android属性动画(ObjectAnimator)的基本用法

来源:互联网 发布:淘宝被骗怎么投诉 编辑:程序博客网 时间:2024/05/22 06:36

本篇博客记录一下Android属性动画的基本用法。


1、单属性变化

Android提供的属性动画机制,可以很简单地调整视图的属性值,形成动画效果。例如:

.............//在3s时间内,将mSunView沿着y轴从start移动到end的位置//其实现的原理是,不断调用mSunView.setY接口,设置其纵坐标//纵坐标的值,逐渐从start变化到endObjectAnimator heightAnimator = ObjectAnimator        .ofFloat(mSunView, "y", start, end)        .setDuration(3000);//也可以设置插值器,例如逐渐加速等heightAnimator.setInterpolator(new AccelerateInterpolator());//动画开始heightAnimator.start();............

ObjectAnimator将根据插值器的规则,将属性值从start逐渐变化到end。

不过有的属性值并不适合逐渐变化,例如颜色。
我们知道颜色是用类似于#fcfcb716这种16进制的数字表示的,如果逐渐增加数字,反而会带来剧烈的色彩变化。
此时ObjectAnimator需要借助TypeEvalutor的子类,精确地计算开始到结束间的递增指。例如:

.............ObjectAnimator skyAnimator = ObjectAnimator        .ofInt(mSkyView, "backgroundColor", start, end)        .setDuration(duration);//借助于ArgbEvaluator,精确调整色彩变化的递进值skyAnimator.setEvaluator(new ArgbEvaluator());skyAnimator.start();.............

2、多属性同时变化

当需要同时调整多个属性时,可以使用PropertyValuesHolder,例如:

................//沿着x轴缩放,缩放比例从startendPropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", start, end);//沿着y轴缩放,缩放比例从startendPropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", start, end);//利用ofPropertyValuesHolder接口,可以传入View对象及多个PropertyValuesHolder//当该动画开始时,mSunView多个的将同时沿着x、y轴缩放ObjectAnimator.ofPropertyValuesHolder(mSunView, pvhX, pvhY)        .setDuration(3000)        .start();..........

为了达到同样的效果,也可以使用AnimatorUpdateListener,例如:

...............ObjectAnimator scaleAnimator = ObjectAnimator        //使用一个不存在的属性,mSunView并不会发生实际的改变        //但生成的值会逐渐从start变化到end        .ofFloat(mSunView, "whatever", start, end)        .setDuration(3000);//增加AnimatorUpdateListener,生成的值变化时,就会回调onAnimationUpdate接口scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    @Override    public void onAnimationUpdate(ValueAnimator animation) {        //得到变化的值        float val = (float)animation.getAnimatedValue();        //调整属性        mSunView.setScaleX(val);        mSunView.setScaleY(val);    }});scaleAnimator.start();..............

3、多属性先后变化

当需要多个属性先后变化时,就可以AnimatorSet了。
AnimatorSet就是可以放在一起执行的动画集,其使用方法类似于:

...................@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void startAnimation(boolean isSunSet) {    //创建动画集    mAnimatorSet = new AnimatorSet();    //heightAnimator将和initialSkyAnimator、scaleAnimator、rotateAnimator同时播放    mAnimatorSet.play(getHeightAnimator(isSunSet))            .with(getInitialSkyAnimator(isSunSet))            .with(getScaleAnimator(isSunSet))            .with(getRotateAnimator())            //先于laterSkyAnimator            .before(getLaterSkyAnimator(isSunSet));    mAnimatorSet.start();}@RequiresApi(api = Build.VERSION_CODES.KITKAT)private ObjectAnimator getHeightAnimator(boolean isSunset) {    float sunYStart = mSunView.getTop();    float sunYEnd = mSkyView.getHeight() + mSunView.getBottom() - mSunView.getTop();    final float start = isSunset ? sunYStart : sunYEnd;    final float end = isSunset ? sunYEnd : sunYStart;    ObjectAnimator heightAnimator = ObjectAnimator            .ofFloat(mSunView, "y", start, end)            .setDuration(3000);    heightAnimator.setInterpolator(new AccelerateInterpolator());    return heightAnimator;}private ObjectAnimator getInitialSkyAnimator(boolean isSunset) {    int start = isSunset ? mBlueSkyColor : mNightSkyColor;    int end = mSunsetSkyColor;    return createSkyAnimator(start, end, 3000);}private ObjectAnimator getLaterSkyAnimator(boolean isSunset) {    int start = mSunsetSkyColor;    int end = isSunset ? mNightSkyColor : mBlueSkyColor;    return createSkyAnimator(start, end, 1500);}private ObjectAnimator createSkyAnimator(int start, int end, int duration) {    ObjectAnimator skyAnimator = ObjectAnimator            .ofInt(mSkyView, "backgroundColor", start, end)            .setDuration(duration);    skyAnimator.setEvaluator(new ArgbEvaluator());    return skyAnimator;}private ObjectAnimator getScaleAnimator(boolean isSunSet) {    float start = (float) (isSunSet ? 1 : 1.5);    float end = (float) (isSunSet ? 1.5 : 1);    PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", start, end);    PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", start, end);    return ObjectAnimator.ofPropertyValuesHolder(mSunView, pvhX, pvhY)            .setDuration(3000);    return scaleAnimator;}private ObjectAnimator getRotateAnimator() {    ObjectAnimator objectAnimator = ObjectAnimator            .ofFloat(mSunView, "rotation", 0, 360)            .setDuration(1000);    objectAnimator.setRepeatCount(4);    return objectAnimator;}.................

4、状态判断和回调接口

AnimatorSet继承Animator,具有同样的接口判断动画执行的状态,例如:

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,        Bundle savedInstanceState) {    View v = inflater.inflate(R.layout.fragment_sunset, container, false);    ..............    v.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {            if (Build.VERSION.SDK_INT >= 19) {                //isStarted判断动画是否已经开始(动画暂停时,isStarted返回的也是true)                if (mAnimatorSet == null || !mAnimatorSet.isStarted()) {                    startAnimation();                } else {                    //isPaused判断动画是否暂停                    if (mAnimatorSet.isPaused()) {                        //resume继续播放动画                        mAnimatorSet.resume();                    } else {                        //pause暂停播放动画                        mAnimatorSet.pause();                    }                }            }        }    });    return v;}

与上述状态变化接口对应,Animator及其子类均可以利用AnimatorListener监听动画的状态:

..............mAnimatorSet.addListener(new Animator.AnimatorListener() {    @Override    public void onAnimationStart(Animator animation) {        ..............    }    @Override    public void onAnimationEnd(Animator animation) {        ..............    }    @Override    public void onAnimationCancel(Animator animation) {        ..............    }    @Override    public void onAnimationRepeat(Animator animation) {        ..............    }});

Animator运行到不同的状态时,将回调AnimatorListener相应的接口。
例如,利用AnimatorListener也可以做到一个动画结束时,启动下一个动画。
只要在AnimatorListener.onAnimationEnd中启动下一个动画即可。


5、逆向播放动画示例

假设现在有个需求,需要点击屏幕后,逆向播放已经放过的动画。
即一个View从高度A下降到B时,点击屏幕,View从B上升到A。

显然动画包含的属性越是复杂,完全逆向就越困难。
不过实现的思路大概是,保存初始和暂停的状态,然后构造逆向的ObjectAnimator,示例如下:

................private long mCurrentPlayTime = 0;private float mCurrentHeight = 0;@RequiresApi(api = Build.VERSION_CODES.KITKAT)private ObjectAnimator getHeightAnimator(boolean isSunset) {    float sunYStart = mSunView.getTop();    float sunYEnd = mSkyView.getHeight() + mSunView.getBottom() - mSunView.getTop();    final float start = isSunset ? sunYStart : sunYEnd;    final float end = isSunset ? sunYEnd : sunYStart;    ObjectAnimator heightAnimator = ObjectAnimator            .ofFloat(mSunView, "y", start, end)            .setDuration(3000);    heightAnimator.setInterpolator(new AccelerateInterpolator());    //利用AnimatorUpdateListener记录动画执行时的中间状态    heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            mCurrentPlayTime = animation.getCurrentPlayTime();            mCurrentHeight = (float)animation.getAnimatedValue();        }    });    heightAnimator.addPauseListener(new Animator.AnimatorPauseListener() {        @Override        public void onAnimationPause(Animator animation) {            heightAnimator = ObjectAnimator                //从当前位置变化到初始位置                .ofFloat(mSunView, "y", mCurrentHeight, start)                //执行时间为已经播放的时间                .setDuration(mCurrentPlayTime);            //逐渐减速            heightAnimator.setInterpolator(new DecelerateInterpolator());            //取消之前的动画            //这里只是示范一下,如果整个AnimatorSet逆向            //则因该在AnimatorSet的AnimatorPauseListener中重构整个AnimatorSet            mAnimatorSet.cancel();            //开始新的动画            heightAnimator.start();        }        @Override        public void onAnimationResume(Animator animation) {        }    });    return heightAnimator;}.........

以上就是属性动画的一些基本用法,以后遇到新的知识再作进一步补充。

P.S. :
最后补充一下,视图中各个属性的基本含义:

0 0