Android动画三:属性动画 (property animation)

参考了郭神的文章,感谢郭神。
因为郭神代码质量高,有部分代码直接借用了郭神相关文章中的,并无盗窃之意,只是学习记录。
郭神博客链接 : http://blog.csdn.net/guolin_blog/

1. API 11(3.0系统)才加入的新特性

注意:属性动画是3.0(API 11)中新加入的特性,在3.0以下系统无法使用。
可以采用开源动画库nineoldandroids来兼容以前的版本,但在3.0以下系统运行时其内部是通过代理View动画来实现的,因此在低版本上本质还是View动画。

2. ValueAnimator

ValueAnimator是整个属性动画机制当中最核心的一个类,初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它本身不作用于任何对象,就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,也就相当于对我们的对象做了动画。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且设定好动画所需的运行时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,
使用示例:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f, 3f, 5f);  
anim.setDuration(1000);  
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        float currentValue = (float) animation.getAnimatedValue();  
        Log.d("Test", "cuurent value is " + currentValue);  
    }  
});  
anim.start(); 

如果希望将一个整数值从0平滑地过渡到100,那么也很简单,只需要调用ValueAnimator的ofInt()方法就可以了,如下所示:

ValueAnimator anim = ValueAnimator.ofInt(0, 100); 

调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放。

ValueAnimator运用类型估值器TypeEvaluator

TypeEvaluator告诉动画系统如何从初始值平滑地过渡到结束值。
前面我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别对浮点型和整型的数据进行动画操作,其实他们都有内置的FloatEvaluator和IntEvaluator来实现这一过渡。我们来看一下FloatEvaluator的代码实现:

public class FloatEvaluator implements TypeEvaluator {  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        float startFloat = ((Number) startValue).floatValue();  
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
    }  
}

实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过渡。
示例:先定义一个Point 类,

public class Point {  

    private float x;  

    private float y;  

    public Point(float x, float y) {  
        this.x = x;  
        this.y = y;  
    }  

    public float getX() {  
        return x;  
    }  

    public float getY() {  
        return y;  
    }  

}  

Point类非常简单,只有x和y两个变量用于记录坐标的位置,并提供了构造方法来设置坐标,以及get方法来获取坐标。接下来定义PointEvaluator,如下所示:

public class PointEvaluator implements TypeEvaluator{  

    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        Point startPoint = (Point) startValue;  
        Point endPoint = (Point) endValue;  
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());  
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());  
        Point point = new Point(x, y);  
        return point;  
    }  

}  

接下来我们就可以非常轻松地对Point对象进行动画操作了,比如说我们有两个Point对象,现在需要将Point1通过动画平滑过度到Point2,就可以这样写:

Point point1 = new Point(0, 0);  
Point point2 = new Point(300, 300);  
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);  
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
            @Override  
            public void onAnimationUpdate(ValueAnimator animation) {  
            //获取到当前进度占整个动画的比例
                float fraction = animation.getAnimatedFraction();
            //获取到当前动画的进度值
                currentPoint = (Point) animation.getAnimatedValue();  
                //利用获取到的currentPoint 生成动画      
            }  
        });  
anim.setDuration(5000);  
anim.start(); 

3. ObjectAnimator

相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,但它其实是继承自ValueAnimator的。
示例:
将textview移除屏幕,再重新移入屏幕。

float curTranslationX = textview.getTranslationX();  
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);  
animator.setDuration(5000);  
animator.start();  

在垂直方向上进行缩放

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 2f, 1f);  
animator.setDuration(5000);  
animator.start(); 

ObjectAnimator可以对任意对象的任意属性做动画,其原理是:要求动画作用对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次调用set方法,随着时间的推移,所传递的值越来越接近最终值。如果我们对object的属性a做动画,动画生效需同时满足以下两个条件:
(1)object必须提供setA方法,如果动画没有传递初始值,还必须提供getA方法,因为系统要去获取属性a的初始值(如果获取不到初始值,程序直接crash)
(2)object的setA方法对属性a所做的改变必须通过某种方式反映出来,例如UI的改变(如果这条不满足,就看不到动画效果)。
View中就因为存在setAlpha()、getAlpha()、setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,ObjectAnimator 才能对View实现4种View动画效果。
针对上述条件2,如果未能满足,有以下3种解决方法:
(1)给对象加上get和set方法,如果有权限的话;
这种方式最简单,但往往都是对原生控件做动画,我们没有权限,都是Android SDK内部实现的方法,无法更改。除非是自定义View。
(2)用一个类来包装原始对象,间接为其提供get和set方法;
示例:

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();
    }
}

通过以上代码就给属性width包装了一层,并给它提供了get、set方法,使用时只需操纵包装类就可以间接调用到get、set方法了,代码示例如下:

ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500),setDuration(5000).start();

以上通过包装mButton,我们在5s内让mButton的宽度增加到了500px。

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

ObjectAnimator运用类型估值器TypeEvaluator

示例:(ColorEvaluator的继承写法略)

ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(),   
    "#0000FF", "#FF0000");  
anim.setDuration(5000);  
anim.start();

4. 时间插值器TimeInterpolator

接口TimeInterpolator的源码定义如下:

/** 
 * A time interpolator defines the rate of change of an animation. This allows animations 
 * to have non-linear motion, such as acceleration and deceleration. 
 */  
public interface TimeInterpolator {  

    /** 
     * Maps a value representing the elapsed fraction of an animation to a value that represents 
     * the interpolated fraction. This interpolated value is then multiplied by the change in 
     * value of an animation to derive the animated value at the current elapsed animation time. 
     * 
     * @param input A value between 0 and 1.0 indicating our current point 
     *        in the animation where 0 represents the start and 1.0 represents 
     *        the end 
     * @return The interpolation value. This value can be more than 1.0 for 
     *         interpolators which overshoot their targets, or less than 0 for 
     *         interpolators that undershoot their targets. 
     */  
    float getInterpolation(float input);  
}  

TimeInterpolator的getInterpolation方法中,参数input由系统计算而得,返回的float数值就是估值器中用到的fraction。可以通过自定义插值器改变返回的fraction,改变动画的加减速效果。然后通过以下代码使自定义的插值器产生作用。

anim.setInterpolator(new myTimeInterpolator());

5. 组合动画

实现组合动画功能主要需要借助AnimatorSet这个类,通过playTogether(), playSequentially(), set.play().with().before().after()这些方法来控制动画的播放顺序。

6. 利用XML文件使用属性动画(路径 //res/animator/anim_demo.xml)

6.1 在xml文件中定义

三种标签:
标签1“ animator”对应代码中的ValueAnimator
标签2“ objectAnimator” 对应代码中的ObjectAnimator
标签3“ set” 对应代码中的AnimatorSet
动画集合中可以再嵌套集合,set标签控制动画顺序的属性为:

anroid:ordering=["together" | "sequentially"]

6.2 在代码中使用定义好的xml文件

示例:

Animator anim = AnimatorInflater.loadAnimator(this, R.animator.anim_demo);
anim.setTarget(mButton);
anim.start();

7. 属性动画的监听回调

Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了
示例:

anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
        //动画开始时
    }  

    @Override  
    public void onAnimationRepeat(Animator animation) {
        //动画重复播放时  
    }  

    @Override  
    public void onAnimationEnd(Animator animation) { 
        //动画结束时 
    }  

    @Override  
    public void onAnimationCancel(Animator animation) {  
        //动画取消时
    }  
});  

为方便开发,系统还提供了一个适配器类,叫作AnimatorListenerAdapter,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画开始这个事件,就只需要单独重写这一个方法就可以了,如下所示:

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
    }  
});  

还有一个监听器AnimatorUpdateListener, 定义如下:

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

这个监听器会监听整个动画过程,我们知道动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被调用一次,利用这个可以做一些特殊的处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值