Android属性动画解析

在手机上实现酷炫的动画效果,是灰常让人赏心悦目的事情。Android系统为我们提供了三种动画:补间动画(Tweened Animation)、帧动画(Frame-By-Frame Animation)、属性动画(Property Animation)。其中,属性动画是在Android3.0才推出的,也是最强大的,所谓“来得早不如来得好”。那么属性动画好在哪里,下面我们就来一探究竟。


属性动画的优点

在属性动画出来之前,补间动画已经能满足我们大多数的需求了,比如平移、旋转、缩放、透明度,但还是有一些硬伤:

首先就是补间动画并没有改变View的属性,所谓平移、旋转、缩放、透明度的变化,只是视觉上的欺骗而已,View的真实位置、大小、透明度、角度这些属性并没有改变,也就是说平移之后,View还在原来的位置,缩放之后,View还是原来的大小……如果是设置了点击事件的View比如Button,我们到Button移动到的新的位置去点击,并不会触发点击事件,而点击Button原来的位置时,仍会触发。而属性动画就是真正的依靠改变View的属性,来不断的做出动画的效果。


其次,补间动画只能对View做动画,有人说,动画不就是对View做才有效果吗?事实上,我们能看到的动画,都是View的动画,但是如果我们能对对象做动画,那么就能实现一些不一样的事。而属性动画,就实现了对对象的动画(属性改变)——也就是属性动画的第一种:ObjectAnimator(对象动画)。当我们想根据一个Point的x、y坐标不断的改变一个View的属性,去做属性动画,就可以用ObjectAnimator。这在只有补间动画的时代,是无法做到的。


最后就是补间动画只能做出平移、旋转、缩放、透明度的改变。有人会说,除了这几种,难道还有其他动画吗?当然,比如我们如果想改变一个View的背景色,让其在某两个色值之间渐变,用补间动画就无法实现,这里就需要用到属性动画的第二种了:ValueAnimator(值动画)。


属性动画的使用

这么好,怎么用?

先说说ObjectAnimator

也是最常用的动画,可以实现补间动画能实现的平移、旋转、缩放、透明度,也可以通过改变View的其他属性来实现一些动画,前提是要改变的属性有set和get方法,这个稍后讲,先看一个最简单的使用:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
animator.setDuration(5000);  
animator.start();  


ObjectAnimator实现动画的核心方法,就是ofFloat,第一个参数,是要做动画的对象;第二个参数,是要改变这个对象的哪个属性,参数类型是String,要实现位移动画就传"translationX"、"translationX",要实现透明度的动画就传"alpha",缩放就传"scaleY"、"scaleY",要改变宽度就传"width",前提是,你要改变的对象有width属性,并且有set和get方法;第三个,第四个是可变长参数,指定要改变属性的初始值、中间值、最终值,中间值可以不指定,也可以指定多个。


这里要说下第二个属性,要实现哪种动画就传对应的属性,而且我们可以传任意字符串,对,没搞错,就是任意。当我们传一个字符串,而对象没有这个属性怎么办呢?这个问题,有两个方面,第一,对象有这个属性,却不是我们想改变的。这时我们可以自定义一个包装类,包裹一下我们的View,在里面设置这个属性,并给出set和get方法,然后再在set方法中,对我们的View相应的属性,进行改变。

比如,我们要改变一个Button的宽度,但是直接传width,改变的是Button上面的文字的宽度,这时我们就可以自己定义一个ViewWrapper包装一下:

private void performAnimate() {  
    ViewWrapper wrapper = new ViewWrapper(mButton);  
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();  
}  
  
@Override  
public void onClick(View v) {  
    if (v == mButton) {  
        performAnimate();  
    }  
}  
  
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();  
    }  
}  


第二种,对象压根没有这个属性,或者这个属性就是我们自己瞎写的。那也没什么,无非就是ObjectAnimator变成ValueAnimator,事实上ObjectAnimator本身就是集成自ValueAnimator的,后面再讲Value Animator的具体用法。


然后再讲讲多个动画一起执行的处理,属性动画有一个集合类,AnimatorSet,可以把想要一起执行的动画全部add进去,然后调用animSet.playTogether(anim1,anim2)方法即可。还可以用ObjectAnimator本身的play(anim1)、with(anim2)、after(anim3)、before(anim4),这几个方法来实现。

下面说ValueAnimator
和ObjectAnimator的实现方法很类似,简单看下Value Animator实现垂直平移的代码:

public void verticalRun(View view)  {  
        ValueAnimator animator = ValueAnimator.ofFloat(0, 400);  
        animator.setTarget(mBtn);  
        animator.setDuration(400).start();  
}  

咋一看是不是跟ObjectAnimator完全一样,哪里有不同?仔细看下会发现,这里只设置了View、初始值、最终值、持续时间,但是并没有设置要改变的属性,比如"translationY",那么毫无疑问,这里是不会有任何动画的视觉效果发生的,怎么才能真正做出动画呢?就需要设置一个监听了。有人会说,好麻烦,还要设置监听,是麻烦了点,但是比起随之而来的灵活性,这算不了什么。比如我们就不再需要做动画的对象有get和set方法,比如我们可以改变对象的背景色,或者在监听的回调中,根据值动画的值的改变,同时改变对象的好几个属性。


实例:我们用值动画让一个小球做自由落体和抛物线运动。

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"   
    android:id="@+id/id_container"  
    
    >  
  
    <ImageView  
        android:id="@+id/id_ball"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:src="@drawable/bol_blue" />  
  
    <LinearLayout  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:layout_alignParentBottom="true"  
        android:orientation="horizontal" >  
  
        <Button  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:onClick="verticalRun"  
            android:text="垂直" />  
  
        <Button  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:onClick="paowuxian"  
            android:text="抛物线" />  
  
    </LinearLayout>  
  
</RelativeLayout>  

左上角一个小球,底部分别是两个Button,一个让小球垂直运动,一个让小球做抛物线运动。

先看垂直:

    /** 
     * 垂直自由落体 
     * @param view 
     */  
    public void verticalRun( View view)  {  
        ValueAnimator animator = ValueAnimator.ofFloat(0, mScreenHeight  
                - mBlueBall.getHeight());  
        animator.setTarget(mBlueBall);  
        animator.setDuration(1000).start();  
//      animator.setInterpolator(value)  
        animator.addUpdateListener(new AnimatorUpdateListener()  
        {  
            @Override  
            public void onAnimationUpdate(ValueAnimator animation)  
            {  
                mBlueBall.setTranslationY((Float) animation.getAnimatedValue());  
            }  
        });  
    }  

虽然我们自己写了监听,在回调中手动改变位置,但是提高了灵活性。

下面看抛物线运动,水平方向速度100px/s,垂直方向加速度200px/s*s。好像只和时间有关系,但是随着时间变化,水平和垂直方向的速度是不一样的,这就要用到TypeEvaluator了,因为我们在时间变化的时候,需要同时返回两个值,一个x,一个y。


     /** 
     * 抛物线 
     * @param view 
     */  
    public void paowuxian(View view)  {  
  
        ValueAnimator valueAnimator = new ValueAnimator();  
        valueAnimator.setDuration(3000);  
        valueAnimator.setObjectValues(new PointF(0, 0));  
        valueAnimator.setInterpolator(new LinearInterpolator());  
        valueAnimator.setEvaluator(new TypeEvaluator<PointF>()  
        {  
            // fraction = t / duration  ,单位时间,时间片段
            @Override  
            public PointF evaluate(float fraction, PointF startValue,  
                    PointF endValue)  
            {  
                Log.e(TAG, fraction + "");  
                // x方向速度100 ,则y方向v = at = 200 * t  
                PointF point = new PointF();  
                point.x = 100 * fraction ;   // s = vt
                point.y = 0.5f * 200 * fraction * fraction ;  // s = 1/2 * a*a * t
                return point;  
            }  
        });  
  
        valueAnimator.start();  
        valueAnimator.addUpdateListener(new AnimatorUpdateListener()  
        {  
            @Override  
            public void onAnimationUpdate(ValueAnimator animation)  
            {  
                PointF point = (PointF) animation.getAnimatedValue();  
                mBlueBall.setX(point.x);  
                mBlueBall.setY(point.y);  
  
            }  
        });  
    }  

上面提到,ObjectAnimator指定的属性字符串不存在时,就变成了ValueAnimator,这时我们就也需要为ObjectAnimator添加监听器了。


参考文章:

Android 属性动画(Property Animation) 完全解析 (上)

Android属性动画完全解析(上),初识属性动画的基本用法

Android属性动画深入分析:让你成为动画牛人

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值