Android 动画——属性动画

    从 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 轴 进行操作):

    

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


扫描二维码关注公众号,回复: 1870604 查看本文章

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

    我们可以看到,缩放平移在一个动画中只能改变 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 ...


    这里说,没有在 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();
    }

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


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

  • 组合动画

    第一种方式:使用 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();
    }

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


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

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

    效果图:


    项目地址:源代码



    

猜你喜欢

转载自blog.csdn.net/young_time/article/details/80880124