属性动画的简单使用和总结

推荐一本书—《android开发艺术探索》,内容很好很强大,这里也算是个读书笔记吧,在此膜拜一下作者刚哥,期待刚哥的配套视频。。。

在API版本11的时候,andriod新引进了一个新的动画:属性动画,相比之前的View动画,属性动画功能强大,它可以对任何对象的任何属性做动画,不仅仅只是view,而且在动画效果上,属性动画也不像view动画那样只局限与平移、缩放、旋转、透明度这四种,各种各样的自定义绚丽的效果都可以实现。
常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet,从类名可以看到,这分别是值动画、对象动画、动画集合。ObjectAnimator对象动画是继承于ValueAnimator动画的

一、ValueAnimator—值动画

单纯的ValueAnimator并不常用,因为只是单纯的对值进行操作而不赋予对象的属性上的话,意义并不大,用法很简单,将一个值在1秒内从0过渡到1,可以这么写:

ValueAnimator ani = ValueAnimator.ofFloat(0f,1f);
        ani.setDuration(1000);
        ani.start();

这样动画就可以运行了,只是看不出来,因为单纯的一个值的变化在屏幕上是一片空白,不过可以通过监听器来监控动画,添加监听有两种方法,addListener和addUpdateListener,分别对应不同的监听器。
addListener对应的是AnimatorListener监听器,可以使用如下代码添加:

ani.addListener(new AnimatorListener() {

            @Override
            public void onAnimationStart(Animator animation) {
                // TODO Auto-generated method stub
                //动画开始的时候,此方法被触发
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                // TODO Auto-generated method stub
                //动画重复的时候,此方法被触发
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                // TODO Auto-generated method stub
                //动画结束的时候,此方法被触发
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                // TODO Auto-generated method stub
                //动画取消的时候,此方法被触发
            }
        });

但是一般情况下,我们并不需要监听这么多状态,所以官方提供了另外一种方式,如下:

ani.addListener(new AnimatorListenerAdapter() {

//          @Override
//          public void onAnimationEnd(Animator animation) {
//              // TODO Auto-generated method stub
//              super.onAnimationEnd(animation);
//              //当动画结束的时候,此方法被触发
//          }

        });

AnimatorListenerAdapter是一个AnimatorListener的适配器类,它已经实现了AnimatorListener的所有接口,所以使用它的时候不必将每一个方法都实现,只需要在需要的地方重写就可以了,其他的都会是默认实现。
PS:此类监听器本人习惯称之为状态监听器(有开始、结束、重复、取消等状态)
系统还提供了另外一种监听方法:addUpdateListener,对应的监听器为AnimatorUpdateListener,使用方法如下:

ani.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // TODO Auto-generated method stub
                float f = (Float)animation.getAnimatedValue();
            }
        });

和上面的状态监听器不同的是,这个AnimatorUpdateListener监听器监听的是动画的实时更新,动画是由很多帧组成了,每播放一帧,onAnimationUpdate方法就会调用一次。
PS:此类监听器本人习惯称为进行时监听器,它监听的是动画的实时播放。
ValueAnimator常用的方法:

ValueAnimator ani = ValueAnimator.ofInt(0,100,200);//对一个int值进行动画过渡
        ani.setDuration(3000);//设置动画播放时长,单位为毫秒      
        ani.setRepeatCount(ValueAnimator.INFINITE);//设置循环次数,-1时则是无限循环,ValueAnimator.INFINITE值就是-1
        ani.setRepeatMode(ValueAnimator.REVERSE);//设置循环模式,有两种,RESTRAT重新播放,REVERSE倒序播放
        ani.setStartDelay(1000);//延时播放
        ani.setEvaluator(new PointEvaluator());//设置自定义估值器
        //设置插值器
        ani.setInterpolator(new TimeInterpolator() {

            @Override
            public float getInterpolation(float input) {
                // TODO Auto-generated method stub
                return 0;
            }
        });

二、ObjectAnimator对象动画

和ValueAnimator值动画相比,ObjectAnimator对象动画用的就比较多,它的功能相当强大,几乎可以对任何对象的任意属性做动画,用法也很简单,如下:

//从当前位置向左移动300距离再移回来
        float currentTranslationX = tv.getTranslationX();
        ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-300f,currentTranslationX); 
        ani.setDuration(2000);
        ani.start();

上面的方法的第一个参数是目标对象,这里是一个textView,第二个参数是目标对象的目标属性,这里是translationX,后面是若干个属性值。translationX属性控制着控件的X坐标,将一个textView的translationX的属性做动画,X坐标不停的变换,那么整个控件就会产生了动画效果。
属性动画机制其实是不停的给对象(比如View)的某个属性赋值(比如translationX),每一次赋值一般都会触发视图重绘(如果没有那么不会产生动画效果,需要自己手动强制重绘),内部原理其实是:属性动画在改变对象的某个属性的时候(比如属性abc),需要对象提供方法setAbc,这样属性动画就会根据外部传进来的初始值和最终值进行多次调用setAbc方法来给相应的属性赋值,这样在一个时间段内,属性abc被多次改变,越来越接近最终值,如果有触发重绘的话,就会产生动画。
两个注意点:
1、 想要通过属性动画来使对象的某个属性abc进行改变,那么对象就要有setAbc方法,同时,外部传值的时候如果没有初始值,那么对象还需要提供getAbc方法用来获取初始值。
2、 想要有动画的效果,那么对象的属性在改变的时候需要造成UI的改变,否则看不到动画效果。
至于监听器方面,由于ObjectAnimator是继承于ValueAnimator,所以用法和ValueAnimator是一样的,不多说。
三、AnimatorSet 动画集合

真正的项目中的动画都是绚丽多彩的,仅仅靠一个动画很难实现,所以动画集合AnimatorSet也很常用,用法也很简单,如下:

//向左移动
        ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-300f,currentTranslationX);
        //透明度变换
        ObjectAnimator ani1 = ObjectAnimator.ofFloat(tv, "alpha", 1f,0f,1f);
        //旋转180度
        ObjectAnimator ani2 = ObjectAnimator.ofFloat(tv, "rotation", 0f,180f);
        AnimatorSet as = new AnimatorSet();
        as.setDuration(2000);
        as.play(ani).with(ani1).after(ani2);//先旋转之后,移动和透明度同时改变,with属于同时播放,after和before属于有序播放
        as.start();

也可以这样:

ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-500f,currentTranslationX);
        ObjectAnimator ani1 = ObjectAnimator.ofFloat(tv, "rotation", 0f,360f);
        ObjectAnimator ani2 = ObjectAnimator.ofFloat(tv, "alpha", 1f,0f,1f);
        AnimatorSet as = new AnimatorSet();
        //有序进行
//      as.playSequentially(ani,ani1,ani2);
        //同时进行
        as.playTogether(ani,ani1,ani2);
        as.setDuration(3000);
        as.start();

方法很简单,AnimatorSet动画集合有两种方式,一个就是有序播放,还一个就是同时播放

四、使用xml方式来播放动画

和之前的view动画一样,属性动画也可以写在xml中,方便复用,代码也很简单,在res目录中创建animator文件夹,创建animator.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" 
    android:ordering="sequentially">


    <animator 
        android:duration="2000"
        android:repeatCount="2"
        android:valueFrom="100"
        android:valueTo="300"
        android:valueType="intType"/>
    <objectAnimator
        android:propertyName="translationX"
        android:duration="3000"        
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType"></objectAnimator>
    <set 
        android:ordering="together">
        <objectAnimator 
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="180"
            android:valueType="floatType"
            android:duration="2000"
            />
        <set android:ordering="sequentially">
            <objectAnimator
                android:propertyName="alpha"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType"
                android:duration="1000"/>
            <objectAnimator 
                android:duration="1000"
                android:propertyName="alpha"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </set>

</set>

其中animator是ValueAnimator,objectAnimator对应的是ObjectAnimator,Set对应的是动画集合AnimatorSet,set动画集合有一个属性是ordering,两个值,sequentially代表的是有序,together代表同时,其他的代码很简单,就不多做解释了,注意一点,当改动的属性propertyName是颜色的时候,valueType就不需要指定了,系统会自动处理。

使用xml方式的动画可以复用,但是加载会稍微慢一些,而且需要提前知道初始值和结束值并且固定,代码中的动画无法复用,但是加载速度快,初始值和结束值不需要定死,实际使用可以按照需求来选择。

五、TypeEvaluater估值器和TimeInterpolator 时间插值器

1、TypeEvaluater估值器
作用是根据当前属性改变的百分比来计算改变之后的属性值
TypeEvaluater是一个接口,里面只有一个方法,如下:

public interface TypeEvaluator<T> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value.
     * @param endValue   The end value.
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public T evaluate(float fraction, T startValue, T endValue);

}

这个方法有三个参数,第一个参数是浮点类型fraction,意味着当前属性改变的百分比,后面两个参数代表这初始对象和终止对象,通过一系列算法最后返回一个同样的对象,这个返回的对象就是当前瞬间的状态,拿系统提供的IntegerEvaluator为例

public class IntEvaluator implements TypeEvaluator<Integer> {

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

这里面的对象就是一个整形,传进去的三个参数中,结束值减去初始值就是整个的变化区间,乘以当前的属性改变百分比,就是当前变化的值,再加上初始值返回,即当前瞬间的值。
初始值和结束值肯定是外部传进去的,那么第一个参数fraction(属性改变的百分比)是从哪里得到的呢,其实是从TimeInterpolator时间插值器中得到的。
2、TimeInterpolator时间插值器
作用是根据时间变化百分比来计算当前属性改变的百分比
同样,先看看接口

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

接口方法也很简单,只有一个方法,方法中的参数input是从系统中获得,这个参数的值随着动画的运行不断变化,并且是匀速变化,可以理解为“时间”,时间流逝是匀速的,这里面就是根据时间流逝百分比来计算属性变化的百分比,返回一个瞬时的浮点型,这个浮点型就是估值器中的fraction。
流程:动画开始–>插值器算法通过时间流逝得到属性变化百分比fraction–>估值器算法通过属性变化百分比得到当前属性值–>动画结束。
常用的系统提供的插值器有:
LinearInterpolator线性插值器(匀速变化)、AccelerateDecelerateIntepolator加速减速插值器、
AccelerateIntepolator加速插值器和DecelerateIntepolator减速插值器等,如果要自定义插值器就需要实现Interpolator或者TimeInterpolator。
常用的系统提供的估值器有:
FloatEvaluator浮点估值、IntEvaluator整形估值、ArgbEvaluator颜色估值器,如果自定义估值器的话就需要实现TypeEvaluator。

六、对任意属性做动画

前面说属性动画原理机制的时候提到过,要想有动画效果,需要两个注意点:
1、 想要通过属性动画来使对象的某个属性abc进行改变,那么对象就要有setAbc方法,同时,外部传值的时候如果没有初始值,那么对象还需要提供getAbc方法用来获取初始值。
2、 想要有动画的效果,那么对象的属性在改变的时候需要造成UI的改变,否则看不到动画效果。
那么如果想要给某个对象做动画,但是对象并没有提供相应的setAbc方法,或者相应的setAbc方法并没有达到想要的效果,比如TextView的setWidtht方法,并不是控制了View的宽度,而是最小和最大宽度,所以这种情况下,就需要自己想办法。
分析要想达到的效果是:宽度不停的变化从而产生动画。
有两种方法可以实现:
1、 对目标对象进行包装,定义set和get方法,在set方法内进行目标对象的属性变化并且更新UI
例如:

private class ViewHolder{
        private TextView tv;
        public ViewHolder(TextView tv){
            this.tv = tv;
        }
        public void setWidth(int width){
            tv.getLayoutParams().width = width;
            tv.requestLayout();
        }
        public int getWidth(){
            return tv.getLayoutParams().width;
        }
        public void setHeight(int height){
            tv.getLayoutParams().height = height;
            tv.requestLayout();
        }
        public int getHeight(){
            return tv.getLayoutParams().height;
        }
    }

在外部调用的时候就需要如此写即可

public void startAnimation(){
        ViewHolder viewHolder = new ViewHolder(tv);
        ObjectAnimator ani = ObjectAnimator.ofInt(viewHolder, "width", 500).setDuration(3000);
        ani.start();
    }

2、 ValueAnimator类中可以添加进行时监听器addUpdateListener,监听每一帧的变化,并且每一帧都会调用,那么直接在方法内添加对对象属性的改变并且重新绘制即可,这样就会在每一帧的时候变化对象的属性,继而产生动画。如下:

public void startAnimation(){
//      ViewHolder viewHolder = new ViewHolder(tv);
//      ObjectAnimator ani = ObjectAnimator.ofInt(viewHolder, "width", 500).setDuration(3000);
//      ani.start();
        ValueAnimator ani = ValueAnimator.ofFloat(0,1);     
        ani.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // TODO Auto-generated method stub
                float f = animation.getAnimatedFraction();
                Log.v("Animator", "属性改变百分比是:"+f);
                tv.getLayoutParams().height = (int)(f*500);
                tv.requestLayout();
            }
        });
        ani.setDuration(3000);
        ani.start();
    }

这个其实就是利用了监听器,进行时监听器监听的是动画的实时播放,所以在方法内做属性的改变,每一帧调用一次,整体就形成了动画…
PS:总感觉类似于“挂载”?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值