android动画入门:属性动画

前言

属性动画是API11加入的新特性,旨在实现更加绚丽的动画效果,不再像 View动画那样只能支持四种简单的变换,其可以对任意对象的属性进行动画而不仅仅局限于View。

由于属性动画从API11才加入,我们可以采用nineoldandroids开源动画库来兼容以前版本,Nineoldandroids的功能和系统原生的android.animation.*中类的功能完全一致,使用方法也完全一样,只要我们用nineoldandroids 来编写动画,就可以在所有的Android系统上运行。

属性动画的使用

下面来集中学习属性动画中的几个常用的动画类:

  • ValueAnimator
  • ObjectAnimator 其继承自 ValueAnimator
  • AnimatorSet 动画集合
1. ValueAnimator

关于ValueAnimator,首先先要理解一个概念,ValueAnimator 的动画并不是直接作用于对象的,而是对一个值做动画,然后我们通过监听这个值得变化,来完成我们对所要变换的对象的属性值的修改,从而实现动画效果。下面通过一个例子,可能会更好理解:

private void startAnimation(final View view, final int start ,final int end){
        ValueAnimator valueAnimator =ValueAnimator.ofInt(1,100);
        valueAnimator.setDuration(3000).start();
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
            
            private IntEvaluator evaluator = new IntEvaluator();
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {                
                //获取当前动画进度的比值
                float fraction = animation.getAnimatedFraction();
                //估值器通过比值计算变换后的宽度
                view.getLayoutParams().width = evaluator.evaluate(fraction, start, end);
                view.requestLayout();
            }
        });
        
    }

从上面的例子可以看出,ValueAnimator 将整数值从1 变换至 100,间隔时间是 3000 毫秒,我们需要添加 AnimatorUpdateListener 来监听这个变换过程,在onAnimationUpdate 方法中,通过获取动画的变换进度来最终确定变换结果,并完成对 对象属性值的修改。

2. ObjectAnimator

ObjectAnimator 继承自 ValueAnimator , 并做了新的封装。ObjectAnimator 允许指定 作用的对象以及变换的属性,我们只需要调用其提供的方法,剩下复杂的变换过程,交给ObjectAnimator来完成即可。
在使用ObjectAnimator之前,我们先来看一下几个常用的View属性成员:

  • translationX,translationY:X/Y 轴方向偏移量的变化,相对于view 自身。
  • rotationX,rotationY:沿 X/Y轴方向旋转。
  • scaleX,scaleY:沿 X/Y 轴缩放。
  • x,y:改变view在整个屏幕上的绝对坐标。
  • alpha:透明度的变化。

下面举例来说明一下:

//沿Y轴向右平移100px ,正数为向右,负数为向左 
ObjectAnimator.ofFloat(view, "translationY", 100).start();

ObjectAnimator类提供了ofInt、ofFloat、ofObject 等常用的方法,接下来我们来看一下这些方法调用时都需要什么参数:

/**
     * 
     * @param target
     * @param propertyName
     * @param values
     * @return
     */
 public static ObjectAnimator ofFloat(Object target, String propertyName, float... values);

从上面这个例子来看,target,propertyName 很好理解,分别是 作用的对象及 对象的属性名。 重点来说一下 , values。values 是可以设置多个的,如下所示:

ObjectAnimator.ofFloat(view, "translationY", 100).start();
ObjectAnimator.ofFloat(view, "translationY", 100,150,200).start();

当只设置一个时就把通过getTranslationY()反射获取的值作为初始值,设置的值作为终点;如果设置两个或多个,那么就会不断的改变属性值,而初始值则为 第一个属性值。

ObjectAnimator是通过不断地调用setXXX方法更新属性值来达到动画的效果的,这就要求Object必须声明有getXXX和setXXX方法。

**在ObjectAnimator 的使用过程中,总会遇到 设置的属性名 在Object 中并不存在,或者没有提供getXXX和setXXX方法,再或者提供了方法并不能实现预期的效果等这类问题。那么下面提供一个解决方法: 我们可以自定义一个类来包装 目标对象,并提供getXXX和setXXX方法。 **
下面通过举例来看一下:

//首先定义包装类
    private static class ViewWrapper{
        private View target;

        private ViewWrapper(View target) {
            super();
            this.target = target;
        }

        public int getWidth() {
            return target.getLayoutParams().width;
        }

        public void setWidth(int width) {
            this.target.getLayoutParams().width = width;
            target.requestLayout();
        }
    }

//直接使用
ViewWrapper viewWrapper = new ViewWrapper(view);
ObjectAnimator.ofFloat(viewWrapper, "width", 100).start();

说到这里,有人想问:如果我想实现同时修改多个属性怎么办呢? 别急,这个时候我们就需要用到 PropertyValuesHolder 类。具体如下:

public static void performAnimate(View view) {

        PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat("scaleX", 1f,0.3f);
        PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat("scaleY", 1f,0.3f);
        PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat("translationY", 0, -250, 50);
        PropertyValuesHolder pvhTX = PropertyValuesHolder.ofFloat("translationX", 0, 200);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
                view, pvhSX, pvhSY, pvhTY, pvhTX).setDuration(1200);
        objectAnimator.setInterpolator(new DecelerateInterpolator());
        objectAnimator.start();
    }

并且PropertyValuesHolder还提供了 ofFloat()、ofInt()以及ofKeyframe 等方法。ofFloat()、ofInt() 与 ObjectAnimator 中的方法是相同使用的,这里不做过多解释,这里重点说一下,ofKeyframe,先来看下面的例子:

 Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
 Keyframe kf1 = Keyframe.ofFloat(.5f, 2f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);

关于Keyframe

//参数 fraction 表示 动画进度 value 表示动画播放到当前进度的参数值
public static Keyframe ofFloat(float fraction, float value) 
3. AnimationSet

AnimationSet 是动画集合,可以定义一组动画,使用起来也是极其简单的。

ObjectAnimator objectAnimatorA = ObjectAnimator.ofFloat(view, "translationY", 100);
        ObjectAnimator objectAnimatorB = ObjectAnimator.ofFloat(view, "scaleY", 0.3f,1.5f);
        
        AnimatorSet animSet = new AnimatorSet();  
        animSet.setDuration(5000);  
//      animSet.playTogether(objectAnimatorA, objectAnimatorB, ...); //多个动画同时执行
        animSet.play(objectAnimatorA).after(objectAnimatorB); //多个动画先后执行
        animSet.start();  
4. 属性动画监听器

这里提一下,属性动画提供了两个接口,用于监听动画的播放过程。

  • AnimatorUpdateListener
    监听动画整个播放过程
objectAnimatorA.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator arg0) {
                // TODO Auto-generated method stub
                
            }
        });
  • AnimatorListener
    用于监听动画的开始,结束,取消以及重复播放。
objectAnimatorA.addListener(new AnimatorListener() {
            
            @Override
            public void onAnimationStart(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationCancel(Animator arg0) {
                // TODO Auto-generated method stub
                
            }
        });

插值器和估值器

关于插值器和估值器 这里主要讲一下基本概念和使用方式。

1. 插值器(Interpolator)

插值器用于实现动画的非匀速变化,比较常见的插值器(Interpolator)有 LinearInterpolator (线性插值器:匀速动画),AcclerateDecelerateInterpolator(加减速插值器:两头慢中间快)和 DecelerateInterpolator(减速插值器:动画速度越来越慢)。

objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
2. 估值器(TypeEvluator)

估值器用于 计算变换后的属性值。通过动画进行过程的百分比,确定当前的属性值。

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

从源码可以看出,其实就是一个简单的估值算法。使用方式在 ValueAnimator 的示例中也有提到:

private void startAnimation(final View view, final int start ,final int end){
        ValueAnimator valueAnimator =ValueAnimator.ofInt(1,100);
        valueAnimator.setDuration(3000).start();
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

            private IntEvaluator evaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {                
                //获取当前动画进度的比值
                float fraction = animation.getAnimatedFraction();
                //估值器通过比值计算变换后的宽度
                view.getLayoutParams().width = evaluator.evaluate(fraction, start, end);
                view.requestLayout();
            }
        });

    }

而 在ObjectAnimator中的使用可以 这样使用:

objectAnimator.setEvaluator(new IntEvaluator());

不过从源码来看,估值器设置与否,实现的计算方式实际上与IntEvaluator是一样的:

public int getIntValue(float fraction) {  
        if (mNumKeyframes == 2) {  
            if (firstTime) {  
                firstTime = false;  
                firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();  
                lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();  
                deltaValue = lastValue - firstValue;  
            }  
            if (mInterpolator != null) {  
                fraction = mInterpolator.getInterpolation(fraction);  
            }  
            if (mEvaluator == null) {  
                return firstValue + (int)(fraction * deltaValue);  
            } else {  
                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();  
            }  
        }  
        //...省略了很多代码  
    }  

属性动画的特殊使用

通过属性动画 为 ViewGroup 的子元素设置动画,相对于View 动画 ,有了更多的选择。
如果想开启默认的效果只需要在布局文件中添加:

android:animateLayoutChanges=”true”

如果想实现更炫的效果,就需要用到LayoutTransition,其有5种动画状态可以编辑:

  • LayoutTransition.APPEARING
    表示 View出现时 自身需要的动画效果
  • LayoutTransition.DISAPPEARING
    表示 View消失时 自身需要的动画效果
  • LayoutTransition.CHANGE_APPEARING
    表示 View出现时 整个容器所有View需要的动画效果
  • LayoutTransition.CHANGE_DISAPPEARING
    表示 View消失时 整个容器所有View消失的动画效果
  • LayoutTransition.CHANGE
    表示 除了以上这些情况,整个容器View发生改变时所需要的动画效果

ok,基础就说这个多,下面来看一下具体使用:
item动画效果的设置:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "alpha", 0F, 1F).
                setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);

容器动画效果的设置:

        //pvhLeft, pvhTop, pvhRight, pvhBottom 必须添加,否则不显示动画效果
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);

        PropertyValuesHolder animator = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, animator).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
        changeIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);

关于容器动画的设置,pvhLeft, pvhTop, pvhRight, pvhBottom 必须添加,不然添加不了动画效果,至于具体原因,大家可以自行学习,实际上我也不知道 >_<。
最后给留一个demo ,LayoutTransition动画的实现:
LayoutTransition动画的具体实现效果

总结

写到这里,属性动画的基本使用就差不多了,我在这里并没有涉及属性动画通过 xml定义的方式,总感觉属性动画通过代码实现会更简单清晰一些,如果大家有兴趣可以去学习一下。

动画其他:View动画和帧动画

你可能感兴趣的:(android动画入门:属性动画)