欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 动画——属性动画

程序员文章站 2024-03-21 11:31:22
...

    从 Android 3.0 就推出了属性动画,在以前使用的是视图动画,那么属性动画的优势在哪呢?都知道,视图动画对于一个 View 的操作仅仅只是一个表象的操作,就是说,视图动画中,仅仅只是对它视图的操作,而它真实的坐标和属性并没有发生变化,那么属性动画就是对一个 View 或者非 View 属性的操作,这一强大功能让它解决了视图动画的缺陷。

    和视图动画一样的是,属性动画(Animator )也分为动画和动画集:ValueAnimator(ValueAnimator 算不上实现动画,ObjectAnimator 和TimeAnimator 继承自 ValueAnimator,一般由前者 ObjectAnimator 和 TimeAnimator 来实现动画,后面会说明 ValueAnimator) 和 AnimatorSet,而 ValueAnimator 又分为 ObjectAnimator 和 TimeAnimator,一般我们都使用 ObjectAnimator。

    属性动画和视图动画一样,都可以使用 xml 和 代码的方式实现,大家可以看一下 视图动画 来了解一下,其 xml 的实现方式是大同小异的。所以这里只对一些 xml 实现方式的不同之处作解释,不会像视图动画那篇做详细说明。

    因为属性动画和视图动画实现方式不太一样,所以我们可能先了解一下代码实现方式,这样对于 xml 理解会更家容易。

  • 代码方法

    ObjectAnimator 有很多实现动画的方法,一般我们使用 ofFloat() 方法:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        throw new RuntimeException("Stub!");
    }

    这个方法被重载了多次,我们一般使用这一个,下面说明一下其参数的意义:

参数名  说明
target  属性动画作用的对象
propertyName  属性名,代表要做什么动画
values  形参,一般来说是传入两个参数,代表从..到..

    下面我们分动画说明一下:

  • 渐变动画:
// 通过代码完成渐变动画
    private void doAlphaByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"alpha",1.0f,0.0f)
                // 设置动画时常
                .setDuration(1000);
        // 设置重复次数
        animator.setRepeatCount(2);
        animator.start();
    }
  • 平移动画:
// 平移 X 轴
    private void doTranslateXByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"translationX",0f,200f)
                .setDuration(1000);
        animator.start();

    }
    // 平移 Y 轴
    private void doTranslateYByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"translationY",0f,200f)
                .setDuration(1000);
        animator.start();
    }
  • 缩放动画:
// 通过代码完成缩放动画
    // 缩放 X 轴
    private void doScaleXByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"scaleX",1f,3f);
        animator.start();
    }
    // 缩放 Y 轴
    private void doScaleByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"scaleX",1,3);
        animator.start();
    }
  • 旋转动画:
// 通过代码完成旋转动画
    private void doRotateByCode(){
        ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"rotation",0f,360f)
                .setDuration(1000);
        animator.start();
    }

    这里我们放一下效果图(此处的平移和缩放,都只对 X 轴 进行操作):

    Android 动画——属性动画

    注意:我们看到,当渐变动画结束后,点击其它动画就没有效果了,这是为什么呢?因为属性动画结束后,作用的对象会保留结束后的状态,所以当前作用对象是全透明的状态,所以看不见,我们试试最后进行渐变动画:

Android 动画——属性动画

    这时候就可以看到完整的动画效果了。

    我们可以看到,缩放平移在一个动画中只能改变 X 轴 或者 Y 轴的值,但是实际需求中一般会要求同时改变,那么在聊组合动画之前,我们可以来说另一个东西。因为属性动画是来对属性进行操作的,而且它作用的不只是 View,所以我们可以对要作用的对象包装一下,来改变包装体的属性,从而达到改变作用对象的效果:

    我们来新建一个类,ScaleImage.java:

public class ScaleImage {
    private ImageView mImageView;
    private float mScale;

    public ScaleImage(ImageView imageView){
        this.mImageView = imageView;
    }

    public float getmScale() {
        return mScale;
    }

    public void setmScale(float mScale) {
        this.mScale = mScale;
        mImageView.setScaleX(mScale);
        mImageView.setScaleY(mScale);
    }
}

    接下来我们对 ScaleImage 进行属性动画:

// 对 ScaleImage 进行属性动画,也叫为 ImageView 添加属性
    private void addProperty(){
        ScaleImage image = new ScaleImage(imageView);
        ObjectAnimator animator =ObjectAnimator.ofFloat(image,"mScale",1.0f,3.0f)
                .setDuration(1000);
        animator.start();
    }

    但是我们可以看到,这里报了一个错误:Could not find property setter method setMScale on ...

Android 动画——属性动画

    这里说,没有在 ScaleImage 里面没有找到该属性的 setter 方法 setMScale(),所以这里我们就明白了,属性动画找属性时是通过你属性的 setter 方法,所以我们设置属性时,一定要与我们要做用的对象中 setter 属性一致,所以当我们需要给一个不熟悉的作用对象设置动画属性时,我们也就知道怎么给它找属性了。

    那我们这里的错误是为什么呢?因为笔者是一键创建 getter 和 setter,这里自动创建时,把 setter 的 setMScale() 的 m 小写了,成了 setmScale() 导致了错误,我们只需要改回来就可以了。

    先把代码放下来:

// 对 ScaleImage 进行属性动画,也叫为 ImageView 添加属性
    private void addProperty(){
        ScaleImage image = new ScaleImage(imageView);
        ObjectAnimator animator =ObjectAnimator.ofFloat(image,"mScale",1.0f,3.0f)
                .setDuration(1000);
        animator.start();
    }

    接下来我们看一看效果图:

Android 动画——属性动画

    我们说了添加属性,就该说一下组合动画了,属性动画有很多组合动画的方式:

  • 组合动画

    第一种方式:使用 AnimatorSet:

// 组合动画方式一
    private void doGroupByCode1(){
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView,"TranslationX",0f,200f)
                .setDuration(1000);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView,"alpha",1.0f,0.0f)
                .setDuration(1000);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(imageView,"rotation",0f,360f)
                .setDuration(1000);
        AnimatorSet set = new AnimatorSet();
        // 串行
        set.playSequentially(animator1,animator2);
        // 并行
        set.playTogether(animator2,animator3);
        set.start();
        
        /**
         * 这里也可以使用这样
          set.play(animator1).before(animator2);
          set.play(animator2).with(animator3);
         * 或者
          set.play(animator2).with(animator3).after(animator1);
         */
    }

    串行的方式就上面两种,使用 playSequentially() 或者 before() 或者 after(),而并行的方式还有几种:

//组合动画并行1
    private void doGroupByCode2(){
        PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX",0f,200f);
        PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("rotation",0f,360f);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView,holder1,holder2);
        animator.start();
    }
    //组合动画并行2
    private void doGroupByCode3(){
        imageView.animate().translationX(200f).rotation(360f).alpha(0.0f).start();
    }

    这里放一下第一种的效果图:

Android 动画——属性动画

    属性动画也是可以设置监听器的:

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

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

            @Override
            public void onAnimationCancel(Animator animator) {
                // 动画取消
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
                // 动画重复
            }
        });

    如果我们用不了这么多方法怎么办呢?我们就不传入 AnimatorListener,而是传入 AnimatorListenerAdapter:

animator.addListener(new AnimatorListenerAdapter() {
            // 想要哪个或者哪几个方法都行
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
            }
        });
  • xml 方法

    和视图动画不一样的是,属性动画的 xml 文件是在 res 目录下新建一个 animator 文件,创建 xml 文件时,如果是单个属性动画,其根目录就是 <objectAnimator> 或者 <animator>,如果是动画集就是 <set>。

    因为属性动画使用 xml 方式实现不同的动画效果方式差不多,这里就对一个动画组合进行讨论,先放代码:

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

    <objectAnimator
        android:valueType="floatType"
        android:propertyName="translationX"
        android:valueFrom="1.0f"
        android:valueTo="200f"
        android:duration="1000"/>
    <set
        android:ordering="together">
        <objectAnimator
            android:valueType="floatType"
            android:propertyName="translationX"
            android:valueTo="-200f"
            android:duration="2000"/>
        <objectAnimator
            android:valueType="floatType"
            android:propertyName="translationY"
            android:valueTo="-200f"
            android:duration="2000"/>
        <objectAnimator
            android:valueType="floatType"
            android:propertyName="rotation"
            android:valueTo="720f"
            android:duration="2000"/>
    </set>
    <objectAnimator
        android:valueType="floatType"
        android:propertyName="alpha"
        android:valueTo="0.0f"
        android:duration="1000"/>
</set>

   对 <set> 的重要属性说明:

属性名  说明
android:ordering=""  动画集的运行方式:并行/串行

    对 <objectAnimator> 的重要属性说明:

属性名  说明
android:valueType=""  属性的类型
android:propertyName=""  属性名
android:valueFrom=""  当前属性动画开始时状态
android:valueTo=""  当前属性动画结束时状态
android:duration=""  动画时常,单位:毫秒
android:repeatCount=""  动画重复次数

    接下来是在代码中加载 xml 动画文件了:

// 使用 xml 进行组合动画
    private void doGroupByXML(){
        Animator animator = AnimatorInflater.loadAnimator(this,R.animator.animator_group);
        // 设置动画作用对象
        animator.setTarget(imageView);
        animator.start();
    }

    效果图:

Android 动画——属性动画

    项目地址:源代码