Android 属性动画

来源:互联网 发布:写作软件app 编辑:程序博客网 时间:2024/06/06 07:27

ValueAnimator是这个动画机制中最核心的部分,他允许在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。

概述

默认时间间隔300ms,帧率为10ms/帧。
常用方法实例:

    ObjectAnimator.ofInt(view,"translationX",100).start();

解释:在300ms内,将view的在X轴上平移100dp,每次移动的单位是整数(int);与之对应的还有ofFloat,每次改变的是小数。

插值器和估值器
插值器TimeInterpolator:作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。部分系统预制的有:

 - LinearInterpolator:线性插值器 均匀动画 - AccelerateDecelerateInterpolator:加速减速插值器,先加速再减速,动画中间快两头慢 - DecelerateInterpolator:减速插值器,速度越来越慢
/**LinearInterpolator 源码 输入值与返回值一致*/public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory{public LinearInterpolator(){    }    public LinearInterpolator(Context context, AttributeSet attrs){    }    public float getInterpolation(float input){        return input;    }    /** @hide */    @Override      public long createNativeInterpolator(){        return NativeInterpolatorFactoryHelper.createLinearInterpolator();    }}
估值器TypeEvaluator :根据当前属性改变的百分比来计算改变后的属性值。预制的有: - IntEvaluator:整型 - FloatEvaluator:float - ArgbEvaluator: 颜色

他们是实现非均匀加速的关键手段。部分源码如下:

public interface TypeEvaluator<T> {   /*** fraction :介于起始值(startValue x0)与最终值(endValue x1)之间的值   * return :x0 + t * (x1 - x0)   */    public T evaluate(float fraction, T startValue, T endValue);}public class IntEvaluator implements TypeEvaluator<Integer> {    public Integer evaluate(floatfraction, Integer startValue, Integer endValue) {        int startInt = startValue;        return (int)(startInt + fraction * (endValue - startInt));    }}

属性动画要求该属性有set和get(可选)方法,插值器和估值器除了系统默认的,还可以自己定义。PS:如果要对其他类型(int float color)做自定义,那么要自定义类型估值算法。
监听器
主要有两个AnimatorUpdateLinstener、AnimatorListener。

    public static interface AnimatorListener {            void onAnimationStart(Animator animation);            void onAnimationEnd(Animator animation);            void onAnimationCancel(Animator animation);            void onAnimationRepeat(Animator animation);        }

具体内容如上,并且系统提供有AnimatorListenerAdapter,他是上者的适配器,我们可以选择性的实现上述4个方法。

    public static interface AnimatorUpdateListener {            void onAnimationUpdate(ValueAnimator animation);        }

逐帧调用,每播放一帧就调用一次。

属性动画原理

属性动画要求作用的对象提供该属性的set/get方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递的值都不同并逐渐接近最终值。

so,要想起作用需满足两个条件:

 1. 必须提供set方法,如果没有提供初始值还需提供get方法,因为系统要去取该属性的初始值(不满足就崩溃); 2. set方法必须起作用,如改变UI之类(不满足就没有效果);

针对这个问题系统提供了三种解决方法

 1. 为该对象加上该属性的set/get方法,如果你有权限的话; 2. 封装,用一个类来包装原始对象,间接为其提供set/get方法; 3. 采用ValueAnimator,监听动画过程,自己实现属性的改变

解释:
方法1:pass 大部分情况下无权限;
方法2:封装 也是就自己实现某属性的set/get方法,只不过set/get是其他手段实现的,特别是set,他要负责把效果展示到UI上;
方法3:上面有说道估值器和插值器,在ValueAnimator的addUpdateListener方法内,先获取当前动画的进度值(animator.getAnimatorValue())和当前比例值(animator.getAnimatorFraction()),然后再使用估值器,根据比例得到当前值,剩下部分与方法二中的set部分方法一致。

注意事项:

 1. OOM 帧动画时图片较多较大容易出现OOM; 2. 内存泄漏 当无线循环的动画在Activity退出时应该及时销毁; 3. 兼容问题; 4. View动画是对动画的影像做动画,实体还在原来的位置。有时会出现动画完成后View无法影藏的现象,及setVisibility(View.GONE)失效了,此时应该清除View动画即可解决问题; 5. 不要使用px; 6. 差异:3.0之前,View动画和属性动画在View的新位置都无法触发单击事件,老位置任可触发;3.0之后属性动画的单击事件触发位置为移动之后的问题,而View动画还是老位置; 7. 硬件加速,开启硬件加速效果会更好。

上述代码实现

  //方法二 自行提供属性的set/get方法。        private void startAnimate(View v){        ViewWapper wapper = new ViewWapper(v);        ObjectAnimator.ofInt(wapper, "width").setDuration(5000).start();    }    public class ViewWapper{        private View target;        public ViewWapper(View v){            this.target = v;        }        //提供get方法            public int getWidth(){            return target.getLayoutParams().width;        }        //提供width 方法 并作出响应            public void setWdith(int wdith){               target.getLayoutParams().width = wdith; //赋值               target.requestLayout();//刷新UI        }    }    //方法三:根据动画过程,自己改变属性值    private void startAnimate2(final View view, final int start, final int end){        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            private IntEvaluator mEvaluator = new IntEvaluator();            public void onAnimationUpdate(ValueAnimator animation){                float fraction = animation.getAnimatedFraction();                view.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);                view.requestLayout();            }        });        valueAnimator.setDuration(5000).start();    }  startAnimate(v);  //方法二调用  startAnimate2(v, v.getWidth(), 500);//方法三调用