Android动画机制-属性动画

目录

一、对任意属性做动画

1、给这个对象加上get和set方法,如果有权限的话

2、用一个类来包装原始对象,间接为其提供get和set方法

3、采用ValueAnimator,监听动画过程,自己实现属性的改变

二、背景色动画

三、制作动画集合

四、使用XML定义属性动画

五、注意事项


        属性动画是API 11新加入的特性,属性动画可以对任何对象做动画,甚至可以没有对象。

        属性动画的原理:属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终的值。总结一下,我们对object的属性abc做动画,如果想要动画生效,要同时满足两个条件:

(1)、object必须要提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取adc属性的初始值(如果这条不满足,程序直接Crash)。

(2)、object的setAbc对属性abc所做的改变必须能够通过某种方法反应出来,比如会带来UI的改变之类的(如果这条不满足,动画无效果,但不会Crash)

                                                                                                                                                            --Android开发艺术探索

一、对任意属性做动画

从前面的满足属性动画所需条件可知,所针对的属性必须有get、set方法。那如果这个属性没有set和get方法呢?

遇到这种情况有三种解决方案:

(1)、给这个对象加上get和set方法,如果有权限的话;

(2)、用一个类来包装原始对象,间接为其提供get和set方法;

(3)、采用ValueAnimator,监听动画过程,自己实现属性的改变。

下面对这三种解决方案进行介绍:

1、给这个对象加上get和set方法,如果有权限的话

        比如我们直接使用LinearLayout,并给它添加个背景,我们知道LinearLayout继承ViewGroup,ViewGroup继承View,ViewGroup和View都是没有setWidth方法的,我们也没有权限去修改LinearLayout的源码实现来给它加上setWidth方法,那这个时候我们如果想要使用这种解决方案的话,我们可以自定义LinearLayout,继承LinearLayout,给它添加个setWidth方法。如下,我们在setWidth方法中设置此控件的布局参数的宽为我们传入的值,然后调用requestLayout重绘。

package com.example.viewanimation;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;

/**
 * 自定义LinearLayout,添加SetWidth方法.
 */

public class AnLinearLay extends LinearLayout {
    public AnLinearLay(Context context) {
        super(context);
    }

    public AnLinearLay(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public AnLinearLay(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setWidth(int width){
        Log.d("AnLinearLay", "setWidth() enter!");
        getLayoutParams().width = width;
        requestLayout();
    }
}

通过调用ObjectAnimator.ofInt让目标LinearLayout在5000ms内宽度增加到500px。

mLin = findViewById(R.id.lin);
ObjectAnimator.ofInt(mLin,"width",500).setDuration(5000).start();

2、用一个类来包装原始对象,间接为其提供get和set方法

还是用LinearLayout的例子,我们前面说过,属性动画可以对任意对象做动画,既然LinearLayout不能直接作用于动画,那我们就用一个类来包装它,在这个类中添加getWidth和setWidth方法,这个时候我们直接属性动画作用这个类的对象,在这个类中操作LinearLayout的宽度。如下,其中target使我们传入的mLin。

    public void performAnimate(View taget){
        ViewWrapper viewWrapper = new ViewWrapper(taget);
        ObjectAnimator.ofInt(viewWrapper,"width",500).setDuration(5000).start();
    }

    private static class ViewWrapper{
        private View mTarget;
        public ViewWrapper(View target){
            mTarget = target;
        }
        public int getWidth(){
            return mTarget.getLayoutParams().width;
        }
        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }

    }

3、采用ValueAnimator,监听动画过程,自己实现属性的改变

        ValueAnimator本身不作用于任何对象,直接使用它是没有任何动画效果的。它可以对一个值多动画,然后我们可以监听器动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的动画做了动画。

实现方法如下,动画的每一帧都会回调onAnimationUpdate方法。每回调一次设置一次宽度。

public void performAnimate(final View target, final int start, final int end){
    ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        //针对整型的估值器,后面需要使用估值器来计算宽度
        private IntEvaluator mEvaluator = new IntEvaluator();
        @Override
            public void onAnimationUpdate(ValueAnimator animation) {
            //获得当前动画的进度值,整型,1-100之间
            int currentValue = (int) animation.getAnimatedValue();
            Log.d(TAG,"current value : " + currentValue);
            //获得当前进度占整个动画过程的比例,浮点型,0-1之间
            float fraction = animation.getAnimatedFraction();
            target.getLayoutParams().width = mEvaluator.evaluate(fraction,start, end);
            target.requestLayout();
        }
    });
    valueAnimator.setDuration(5000).start();
}
mBt1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        performAnimate(mLin, mLin.getWidth(), 500);
    }
});

二、背景色动画

        给一个view的背景色做动画,如下:

//设置button的背景颜色从0xFFFF8080渐变到0xFF8080FF
ValueAnimator colorAnim = ObjectAnimator.ofInt(button,"backgroundColor",0xFFFF8080,0xFF8080FF);
colorAnim.setDuration(3000);
//设置针对color属性的估值器
colorAnim.setEvaluator(new ArgbEvaluator());
//设置重复次数为无限次
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
//设置重复模式为反转
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();

三、制作动画集合

 AnimatorSet animatorSet = new AnimatorSet();
 animatorSet.playTogether(
     ObjectAnimator.ofFloat(button,"rotationX",0,360),
     ObjectAnimator.ofFloat(button,"rotationY",0,180),
     ObjectAnimator.ofFloat(button,"rotation",0,-90),
     ObjectAnimator.ofFloat(button,"translationX",0,90),
     ObjectAnimator.ofFloat(button,"translationY",0,90),
     ObjectAnimator.ofFloat(button,"scaleX",1,1.5f),
     ObjectAnimator.ofFloat(button,"scaleY",1,0.5f),
     ObjectAnimator.ofFloat(button,"alpha",1,0.25f,1)
 );
 animatorSet.setDuration(5 * 1000).start();

四、使用XML定义属性动画

xml文件路径:res/animator/

比如在创建文件res/animator/property_animator.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<objectAnimator
		android:propertyName="x"
		android:duration="3000"
		android:valueTo="200dp"
		android:valueType="floatType"/>
	<objectAnimator
		android:propertyName="y"
		android:duration="3000"
		android:valueTo="300dp"
		android:valueType="floatType"/>
</set>

在代码中加载并开启动画

AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(getApplication(),R.animator.property_animator);
animatorSet.setTarget(button);
animatorSet.start();

五、注意事项

        如果使用属性动画中的一种无限循环的动画,那么在Activity退出时需要及时停止,避免内存泄漏。

colorAnim.end();
colorAnim.cancel();

这两个方法最后都会调用endAnimation()来结束动画,区别就是end方法执行后,动画会定格到动画结束的效果;cancel执行后,动画会定格到此时动画执行到的效果。

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页