Android动画(Android Animation)笔记

demo源码地址:https://github.com/totond/LearnAnimation

  Android里动画时非常常见的,几乎每个炫酷的自定义控件都涉及动画。Android里面的动画分为两类:属性动画(Property Animation)视图动画(View Animation)
  其中Property Animation是Android3.0(API 11)之后才出现的,而View动画只适用于View,包括:补间动画(Tween Animation)、逐帧动画(Frame Animation)

分类和区别

  两者最大的区别就是视图动画(View Animation)是在动画完成后,无论动画结果在哪里,这个View都是不变的,响应区域都是在原地,不会根据结果而移动,想要真正移动的话就要改变它的属性。而属性动画就系在View移动的时候真正地改变它的属性,动画停止之后,View的位置就是当前停止的位置,还可以实现3d操作,而且属性动画作用对象不仅止于View。

视图动画(View Animation)

  只适用于View的动画,包括补间动画(Tween Animation)和逐帧动画(Frame Animation),都可以用XML实现和Java代码。

补间动画(Tween Animation)

  补间动画就是指只要我们指定好动画的开始和结束画面,中间的动画系统就会自动帮我们补全。

补间动画创建步骤
  1. 创建所需要实现动画的View
  2. XML实现:在res的anim里文件夹里创建相应的动画xml文件,设置好相应的参数,然后在Java代码里面新建Animation对象并用AnimationUtils.loadAnimation()方法读取xml动画内容;Java实现:新建Animation对象,在new的时候传入相应的特有参数和通过Animation.setXX()的方法设置好参数。
  3. 通过startAnimation()播放动画。
所有补间动画的共有属性

  下面是所有类型的补间动画的共有属性,都是继承自Animation类的属性,都可以在Java代码通过Animation.setXX()方法或者在XML里通过设置下面的参数设置:

属性作用
android:duration动画持续时间(单位ms)
android:startOffset动画延迟开始时间(单位ms)
android:fillBefore动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode选择重复播放模式,restart代表正序重放,reverse代表倒序回放,默认为restart
android:repeatCount重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator设定插值器,后面详细讲
透明度改变动画Alpha

  渐变透明度动画,可以把一个View进行透明度渐变,下面就是把View的颜色变得越来越浅,最后到没有。

XML实现:

  创建一个名为alpha的xml文件在res的anim里:

<?xml version="1.0" encoding="utf-8"?>

    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
    <!--
        fromAlpha:开始时透明度
        toAlpha: 结束时透明度
        -->

  然后,调用XML动画就行了:

        Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha);
        view_test.startAnimation(alphaAnimation);
Java代码实现:
                Animation alphaAnimation = new AlphaAnimation(1,0);
                alphaAnimation.setDuration(1000);
                view_test.startAnimation(alphaAnimation);
实现效果(两种方式都一样的效果):

旋转动画Rotate
XML实现:

  创建一个名为rotate的xml文件在res的anim里:

<?xml version="1.0" encoding="utf-8"?>

    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromDegrees="0"
        android:toDegrees="270"
        android:pivotX="50%"
        android:pivotY="0"/>

    <!--
        fromDegrees 动画开始时的角度
        toDegrees 动画结束时物件的旋转角度,正代表顺时针
        pivotX 属性为动画相对于物件的X坐标的开始位置,可以设置为数字
        pivotY 属性为动画相对于物件的Y坐标的开始位置
    -->

  这里的pivotXpivotY,可以设置为数字,百分比,或者百分比p,例如分别把两个参数
- 设置为50时,旋转点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE
- 设置为50%时,旋转点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF
- 设置为50%p时,旋转点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT

  然后,调用XML动画就行了:

         Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate);
         view_test.startAnimation(rotateAnimation);
Java代码实现
                Animation rotateAnimation = new RotateAnimation(0,270,0.5f,0);
                rotateAnimation.setDuration(1000);
                view_test.startAnimation(rotateAnimation);
实现效果(两种方式都一样的效果):

尺寸缩放动画效果Scale

XML实现:

  创建一个名为scale的xml文件在res的anim里:

<?xml version="1.0" encoding="utf-8"?>
    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="2"
        android:toYScale="2" />
    <!--
        fromXScale,fromYScale, 动画开始前X,Y的缩放倍数
        toXScale,toYScale, 动画最终缩放的倍数
        pivotX, pivotY 动画起始位置,相对于屏幕的百分比,两个都为50%表示动画从自身中间开始
    -->

  然后,调用XML动画就行了:

                Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale);
                view_test.startAnimation(scaleAnimation);
Java代码实现
                Animation scaleAnimation = new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                scaleAnimation.setDuration(1000);
                view_test.startAnimation(scaleAnimation);
实现效果(两种方式都一样的效果):

位置移动动画translate

XML实现:

  创建一个名为translate的xml文件在res的anim里:

<?xml version="1.0" encoding="utf-8"?>
    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100%"
        android:toYDelta="100%" />

    <!--
        fromXDelta,fromYDelta 起始时X,Y座标
        toXDelta, toYDelta 动画结束时X,Y的座标
        上面这几个都是可以设置为数字,百分比,或者百分比p,用法和rotate一样
    -->

  然后,调用XML动画就行了:

                Animation translateAnimation = AnimationUtils.loadAnimation(this, R.anim.translate);
                view_test.startAnimation(translateAnimation);
Java代码实现
                Animation translateAnimation = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_SELF,0,
                        TranslateAnimation.RELATIVE_TO_SELF,1,
                        TranslateAnimation.RELATIVE_TO_SELF,0
                        ,TranslateAnimation.RELATIVE_TO_SELF,1);
                translateAnimation.setDuration(1000);
                translateAnimation.setDuration(1000);
                view_test.startAnimation(translateAnimation);
实现效果(两种方式都一样的效果):

组合动画Set

  前面讲的都是单个动画效果,然而实际需求中大部分都是需要结合起来使用的,所以就用到Set。Set对于Animation相当于View对于ViewGroup,把里面的动画结合起来(播放的时候是同一时间开始,所以要想不同时间开始的话就要用到android:startOffset)。

XML实现:

创建一个名为translate的xml文件在res的anim里:

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

    <rotate
        android:duration="1000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatMode="restart"
        android:repeatCount="infinite"
        />

    <translate
        android:duration="10000"
        android:fromXDelta="-50%p"
        android:fromYDelta="0"
        android:toXDelta="50%p"
        android:toYDelta="0" />

    <alpha
        android:startOffset="7000"
        android:duration="3000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />

    <scale
        android:startOffset="4000"
        android:duration="1000"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.5"
        android:toYScale="0.5" />

    <scale
        android:startOffset="5000"
        android:duration="1000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="2"
        android:toYScale="2" />
</set>

PS:在Set组合动画里面scale标签里面设置的android:repeatCountandroid:fillBefore是无效的,所以如果需要重复播放或者回到原位的话需要在set标签里设置,但是由于这里rotate标签里面已经设置repeatCount为infinite,所以动画不会结束,也就看不到重播和回复原位(在这里纠结了一下,做下笔记)

Java代码实现
                AnimationSet setAnimation = new AnimationSet(true);

                Animation alpha = new AlphaAnimation(1,0);
                alpha.setDuration(3000);
                alpha.setStartOffset(7000);

                Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                rotate.setDuration(1000);
                rotate.setRepeatMode(Animation.RESTART);
                rotate.setRepeatCount(Animation.INFINITE);

                Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
                        TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
                        TranslateAnimation.RELATIVE_TO_SELF,0
                        ,TranslateAnimation.RELATIVE_TO_SELF,0);
                translate.setDuration(10000);

                Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                scale1.setDuration(1000);
                scale1.setStartOffset(4000);
                Animation scale2 = new ScaleAnimation(1,2,1,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
                scale2.setDuration(1000);
                scale2.setStartOffset(5000);

                setAnimation.addAnimation(alpha);
                setAnimation.addAnimation(rotate);
                setAnimation.addAnimation(translate);
                setAnimation.addAnimation(scale1);
                setAnimation.addAnimation(scale2);
                //因为在Rotate那里设置了RepeatCount为INFINITE,所以动画还不会结束,不会重复播放一次
                // 所以下面两行的设置可以说是无效的,在这里纠结了一下,做下笔记
                setAnimation.setRepeatMode(Animation.RESTART);
                setAnimation.setRepeatCount(1);


//                Animation setAnimation = AnimationUtils.loadAnimation(this,R.anim.set);
                view_test.startAnimation(setAnimation);
动画监听器

  可以通过在Java代码里Animation.setAnimationListener()方法设置。

        Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                //动画开始时执行
            }

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

            @Override
            public void onAnimationRepeat(Animation animation) {
                //动画重复时执行
            }
        }
动画插值器Interpolator

  由于补间动画是用开始第一帧和最后一帧来确定一个动画,中间的画面由系统补全,如果我们想控制中间画面以怎么样的方式补全,我们就要使用插值器(Interpolator)了。

  下面是官方给的9个插值器:

名称ID作用
AccelerateDecelerateInterpolator@android:anim/accelerate_decelerate_interpolator先加速再减速
AccelerateInterpolator@android:anim/accelerate_interpolator加速
AnticipateInterpolator@android:anim/anticipate_interpolator先退后一小步然后加速前进
AnticipateOvershootInterpolator@android:anim/anticipate_overshoot_interpolator先退后一小步然后加速前进,超出终点一小步后再回到终点
BounceInterpolator@android:anim/bounce_interpolator最后阶段弹球效果
CycleInterpolator@android:anim/cycle_interpolator周期运动
DecelerateInterpolator@android:anim/decelerate_interpolator减速
LinearInterpolator@android:anim/linear_interpolator匀速
OvershootInterpolator@android:anim/overshoot_interpolator快速到达终点并超出一小步最后回到终点

  使用的时候可以在XML里的android:interpolator里面设置,或者在Java代码Animation.setInterpolator()里设置

各个插值器的效果一览:

自定义插值器

  如果觉得官方提供的9个插值器不够,还可以自己继承Interpolator做出自定义插值器,这里暂时就不具体说了。

参考资料

http://www.cnblogs.com/whoislcj/p/5730520.html
http://blog.csdn.net/dmk877/article/details/51912104

逐帧动画(Frame Animation)

  逐帧动画顾名思义就是一张一张图片轮着放的动画,类似与电影的播放模式,适合于表现细腻的动画,例如人物动作。

XML实现

  把自己弄到的逐帧动画资源(怎么弄,在网上随便找个gif然后用GifSplitter之类的软件分解成一张张图片就行)放到drawable文件夹里面,注意命名要不能有大写,不能纯数字,我创建了一个knight_attack.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true"
    >
    <item android:drawable="@drawable/a0" android:duration="100"/>
    <item android:drawable="@drawable/a1" android:duration="100"/>
    <item android:drawable="@drawable/a2" android:duration="100"/>
    <item android:drawable="@drawable/a3" android:duration="100"/>
    <item android:drawable="@drawable/a4" android:duration="100"/>
    <item android:drawable="@drawable/a5" android:duration="100"/>
    <item android:drawable="@drawable/a6" android:duration="100"/>
    <item android:drawable="@drawable/a7" android:duration="100"/>
    <item android:drawable="@drawable/a8" android:duration="100"/>
    <item android:drawable="@drawable/a9" android:duration="100"/>
    <item android:drawable="@drawable/a10" android:duration="100"/>
    <item android:drawable="@drawable/a11" android:duration="100"/>
    <item android:drawable="@drawable/a12" android:duration="100"/>
    <item android:drawable="@drawable/a13" android:duration="100"/>
    <item android:drawable="@drawable/a14" android:duration="100"/>
    <item android:drawable="@drawable/a15" android:duration="100"/>
    <item android:drawable="@drawable/a16" android:duration="100"/>
    <item android:drawable="@drawable/a17" android:duration="100"/>
    <item android:drawable="@drawable/a18" android:duration="100"/>
    <item android:drawable="@drawable/a19" android:duration="100"/>
    <item android:drawable="@drawable/a20" android:duration="100"/>
    <item android:drawable="@drawable/a21" android:duration="100"/>
    <item android:drawable="@drawable/a22" android:duration="100"/>
    <item android:drawable="@drawable/a23" android:duration="100"/>
    <item android:drawable="@drawable/a24" android:duration="100"/>
    <item android:drawable="@drawable/a25" android:duration="100"/>
</animation-list>

PS:android:oneshot是设置是否只播放一次,默认是false,item自然是图片资源和duration设置一帧持续时间(ms)

  然后就是在Java代码里面载入并调用start或stop方法了:

import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class FrameActivity extends AppCompatActivity {
    private Button btn_startFrame,btn_stopFrame;
    private ImageView iv;
    private AnimationDrawable animationDrawable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame);

        iv = (ImageView) findViewById(R.id.iv);
        btn_startFrame = (Button) findViewById(R.id.btn_startFrame);
        btn_stopFrame = (Button) findViewById(R.id.btn_stopFrame);


        //开始动画
        btn_startFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                iv.setImageResource(R.drawable.knight_attack);
                animationDrawable = (AnimationDrawable) iv.getDrawable();
                animationDrawable.start();
            }
        });
        //停止动画
        btn_stopFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                iv.setImageResource(R.drawable.knight_attack);
                animationDrawable = (AnimationDrawable) iv.getDrawable();
                animationDrawable.stop();
            }
        });

    }
}
Java实现

  和XML方法相比就是,AnimationDrawable的创建方式不同了而已,这里是直接从drawable文件夹里面取资源

        //获取资源
        animationDrawable = new AnimationDrawable();
        for (int i = 0; i <= 25; i++) {
            int id = getResources().getIdentifier("a" + i, "drawable", getPackageName());
            Drawable drawable = getResources().getDrawable(id);
            animationDrawable.addFrame(drawable, 100);
        }

        //开始动画
        btn_startFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animationDrawable.setOneShot(true);
                iv.setImageDrawable(animationDrawable);
                //在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
                animationDrawable.stop();
                animationDrawable.start();
            }
        });

        //停止动画
        btn_stopFrame.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animationDrawable.setOneShot(true);
                iv.setImageDrawable(animationDrawable);
                animationDrawable.stop();
            }
        });
效果一览:

参考资料

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1106/1915.html
http://www.cnblogs.com/whoislcj/p/5733740.html

View动画总结

  Tween动画有4种单一动画类型:尺寸缩放动画(ScaleAnimation)、透明度改变动画(AlphaAnimation)、旋转动画(RotateAnimation)、平移动画(TranslateAnimation),还有可以通过AnimationSet来将各种单一动画结合组成组合动画。适合用于一些比较简单的动画,如:
- 界面切换的平移动画
- 自定义控件的一些效果:晃动,旋转,淡化之类的

  Frame动画需要一整套的图片作为资源,一帧一帧地播放,所以适用于一些比较复杂细腻的动画,如:
- 自定义效果的进度条
- 复杂图案的复杂旋转效果(如立体旋转之类的)

  除了上面的例子之外,View动画还有很广泛的应用,人的创意是无限的,我们只要学好基础,到时灵感一来就可以有能力抓住。

属性动画

View动画加手动更新属性

  前面说过,使用View动画时,无论动画结果在哪里,这个View都是不变的,响应区域都是在原地,不会根据结果而移动,想要真正移动的话就要改变它的属性。就像这个例子一样:

  这个样子改变了之后,并没有改变它原来的属性,那么我们就

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class PropertyActivity extends AppCompatActivity implements View.OnClickListener{
    private RelativeLayout btLayout;
    private Button btn_test;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_property);
        btLayout = (RelativeLayout) findViewById(R.id.activity_property);
        btn_test = (Button) findViewById(R.id.btn_test);

        btLayout.setOnClickListener(this);
        btn_test.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.activity_property:
                displayToast("点击了底部的Layout");
                break;
            case R.id.btn_test:
                displayToast("点击了test按钮");
                //用Tween动画做出位移
                Animation translateAnimation = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_SELF,0,
                        TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
                        TranslateAnimation.RELATIVE_TO_SELF,0
                        ,TranslateAnimation.RELATIVE_TO_PARENT,0.5f);
//                translateAnimation.setFillEnabled(true);
//                translateAnimation.setFillAfter(true);
                translateAnimation.setDuration(5000);
                translateAnimation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        System.out.println("动画完成");
                        updateParams();
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
                btn_test.startAnimation(translateAnimation);
                break;
        }
    }

    private void updateParams(){
        int left = btLayout.getWidth()/2;
        int top = btLayout.getHeight()/2;
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) btn_test.getLayoutParams();
        params.leftMargin = left;
        params.topMargin = top;
        btn_test.setLayoutParams(params);
    }

    //用于展示Toast
    private void displayToast(String str){
        Toast.makeText(this,str,Toast.LENGTH_SHORT).show();
    }
}

  这样就是通过手动更新(在动画完成的时候实现updateParams()方法,改变Button的属性)来实现真正的移动。

  然而,这样做是比较麻烦的,而且可拓展性也不高,实现一个简单的动画可以用这种手动更新属性,其他的还是用属性动画吧。

自动更新的属性动画

  属性动画其实就是相当于Tween东西的升级版,可以实现Tween动画不能实现的东西,如自动更新属性,3d动画之类的。下面就来看看属性动画:

ValueAnimator

  ValueAnimator是属性动画机制当中最核心的一个类,它控制着属性的值变化,下面是一个值从0到5的在5秒内平滑变化例子:

                ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 5f);
                valueAnimator.setDuration(5000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        btn_testValue.setText(animation.getAnimatedValue().toString());
                    }
                });
                valueAnimator.start();

    public static ValueAnimator ofFloat(float... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setFloatValues(values);
        return anim;
    } 

  从ValueAnimator.ofFloat()源码看出,方法可输入的参数是类型是float...,也就是说可以输入一个或多个float类型参数:输入一个的话,经测试过是过了一个极小的时间后从0开始;输入多个的话就是从第一个参数的值先平滑过渡到后面一个参数的值,然后在平滑过渡到下一个的值,直到最后一个参数。
  例如:

                ValueAnimator valueAnimator = ValueAnimator.ofFloat(1,5,3,10);

  这样就是从1到5再到3最后到10。
  除了float类型的,还有Int类型的ValueAnimator.ofInt(int... values)和Object类型的ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)
  第一个很好理解,是Int类型数值的平滑过渡,第二个也可以看出来是对象的平滑过渡,但是由于Object不像数值那样,可以直接赋值,所以就需要一个TypeEvaluator这样的“赋值器”,让它告诉系统要怎样生成这种Object(具体的后面再说)。

ObjectAnimator

  ObjectAnimator是属性动画中最常用的类,因为前面的ValueAnimator只是对值的操纵,而ObjectAnimator是对任意对象的属性值操纵,所以也就可以看出,它也是基于ValueAnimator的,实际上也是继承自它,相当于把ValueAnimator封装。使用方法也很简单:

                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btn_testObject,"rotation",0,360);
                objectAnimator.setDuration(5000);
                objectAnimator.start();

  ObjectAnimator.ofFloat(Object target, String propertyName, float... values)这里的第一个参数是要操作的对象Object,第二个是要操作的对象的属性名称String(这里rotation是旋转角度属性),后面是任意个数的对象参数值float…(这里0,360就是从0开始到360)。


  View一般有以下的这些属性:

属性作用数值类型
Alpha控制View的透明度float
TranslationX控制X方向的位移float
TranslationY控制Y方向的位移float
ScaleX控制X方向的缩放倍数float
ScaleY控制Y方向的缩放倍数float
Rotation控制以屏幕方向为轴的旋转度数float
RotationX控制以X轴为轴的旋转度数float
RotationY控制以Y轴为轴的旋转度数float
set方法的问题:

  要注意,这里的对象属性,指的不一定是操作对象的属性,是指有set方法的属性,也就是说,只要这个View有setXX()的方法,这里的第二个参数传入XX的话,就相当于在动画操作的时候不断调用这个setXX()方法,知道这些,我们做自定义View的时候就可以注意了。
  另外,对于一些没有set方法的属性,又不能改源码令它有set方法的(用button的width属性),可以选择建立一个包装类,自定义一个set方法,把改变这个属性的过程包装起来,再使用ObjectAnimator。

这里set方法的格式是:setXX()——XX的每个单词首字母要大写s,其余字母小写,然后对应在ObjectAnimator.ofFloat()要传入的参数就是XX,首字母大小写都可以,如setMyName()就对应MyName或者myName

get方法的问题:

  前面说到ObjectAnimator.ofFloat方法后面的参数float…是任意个数的参数值,当个数为1的时候,就会是属性的默认值到这个参数,然而这个默认值是用getXX()的方法取出来的,类似于上面的setXX()方法。所以,需要用到从默认值开始动画的情况,需要确保get方法的存在,不然会报错。

组合动画

  前面讲的都是单一类型的动画,但是一些炫酷的动画总是由多种动画组合起来的,属性动画里实现组合动画的类是AnimatorSet,它有两个方法可以设置动画组合:

public void playTogether(Animator... items)         //设置一起执行的动画
public void playSequentially(Animator... items)     //设置依次执行的动画

  除此之外,还可以使用流水线模式来设置动画组合,下面的方法返回的都是一个AnimatorSet.Builder对象:

public Builder play(Animator anim)          //执行动画,返回一个Builder对象
public Builder with(Animator anim)          //将现有动画插入到传入的动画之后执行,返回一个Builder对象
public Builder after(long delay)            //将现有动画延迟指定毫秒后执行,返回一个Builder对象
public Builder after(Animator anim)         //将现有动画插入到传入的动画之前执行,返回一个Builder对象
public Builder before(Animator anim)        //将现有动画和传入的动画同时执行,返回一个Builder对象

  也就是说,执行动画的顺序是:

-->after()里面的动画 -->play(),with()里面的动画 --> before()里面的动画

  要注意的是,上面的5个方法中,只有play()方法是AnimatorSet的方法,所以需要用它来开始流水线模式,每有一个play(),before(),after()方法,都会占用一段Duration时间。
  下面是例子:

                float width = btLayout.getWidth()/2;
                float heigth = btLayout.getHeight()/2;
                ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(btn_testSet,"scaleX",1,2,1);
                ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(btn_testSet,"rotation",0,360);
                ObjectAnimator TranslationXAnimator = ObjectAnimator.ofFloat(btn_testSet,"TranslationX",width);
                ObjectAnimator TranslationYAnimator = ObjectAnimator.ofFloat(btn_testSet,"TranslationY",-heigth);
                AnimatorSet animatorSet1 = new AnimatorSet();
                //这三个动画同时执行
                animatorSet1.playTogether(scaleXAnimator,rotationAnimator,TranslationYAnimator);
                AnimatorSet animatorSet2 = new AnimatorSet();
                //先执行完位移动画,再执行同时的三个动画
                animatorSet2.play(TranslationXAnimator).before(animatorSet1);
                animatorSet2.setDuration(3000);
                animatorSet2.start();
                break;

  所以这个例子的动画花费了3000*2 = 6000ms来执行。

Animator监听器

  View动画里面,有着一个监听器来监听动画的开始、结束和重放的事件,属性动画里面也有:

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

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

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

                    @Override
                    public void onAnimationRepeat(Animator animation) {
                        //动画重放时执行
                    }
                });

  有很多时候,我们会觉得麻烦,并不需要监听这么多个事件,只需要要里面的一个或者几个就够了,就可以用AnimatorListenerAdapter()

                animatorSet2.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                    }

                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                    }
                });

XML实现

  和View动画一样,属性动画也可以通过XML定义,可以放在res/animator文件夹里面。

XML实现ValueAnimator

  值从0到5的平滑过渡:

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="5"  
    android:valueType="floatType"/>  
XML实现ObjectAnimator

  从0到360的旋转:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="360"  
    android:valueType="floatType"  
    android:propertyName="rotation"/> 
XML实现组合动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="3000"
        android:valueTo="384"
        android:propertyName="translationX"
        android:valueType="floatType" >
    </objectAnimator>

    <set android:ordering="together">
        <objectAnimator
            android:duration="3000"
            android:valueTo="-488"
            android:valueType="floatType"
            android:propertyName="translationY">
        </objectAnimator>

        <objectAnimator
            android:duration="3000"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType"
            android:propertyName="rotation">
        </objectAnimator>

        <objectAnimator
            android:duration="1500"
            android:valueFrom="1"
            android:valueTo="2"
            android:valueType="floatType"
            android:propertyName="scaleX">
        </objectAnimator>
        <objectAnimator
            android:startOffset="1500"
            android:duration="1500"
            android:valueFrom="2"
            android:valueTo="1"
            android:valueType="floatType"
            android:propertyName="scaleX">
        </objectAnimator>

    </set>

</set>

  配置好XML文件之后,要在Java代码里载入并执行:

                Animator animator = AnimatorInflater.loadAnimator(PropertyActivity.this,R.animator.animator_set);
                animator.setTarget(btn_testSet);
                animator.start();

  这些XMl的效果效果和上面的Java代码实现的差不多。总的来说,XML实现比Java代码实现直观了很多,但是相对来说的话就没有那么灵活了,就例如上面set组合动画里的改变scaleX属性由1到2再到1,这里需要两个objectAnimator,而用代码实现仅仅是加多一个参数的事情。Java代码实现还有一个好处就是可以很方便地获取其他控件的属性,没有XML那么死板要写死数值。

插值器TimeInterpolator

  和View动画一样,属性动画的也有插值器,不过它的是新一代插值器TimeInterpolator,不但向前兼容了以前的9个自带的插值器,还新增了一些新的插值器。

  使用的时候只需要setInterpolator()就行了,如:

animator.setInterpolator(new AccelerateDecelerateInterpolator());

参考资料

http://blog.csdn.net/guolin_blog/article/details/43536355
http://blog.csdn.net/xyz_lmn/article/details/38667899
http://blog.csdn.net/singwhatiwanna/article/details/17853275
http://blog.csdn.net/harvic880925/article/details/50072739

源码地址:https://github.com/totond/LearnAnimation

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值