Android Property Animator (属性动画)第一篇

1、前言

每款产品都会有动画的身影,怎么才能做出绚丽的,多样的动画。那么这篇文章会对你有所帮助,别把动画想得很难,其实它很简单,我们一起来看一看:

Android Property Animator (属性动画)第一篇_第1张图片

Android Property Animator (属性动画)第一篇_第2张图片

看完后面的内容,制作出上面两种动画效果已经不是问题。我至今还是苦恼的一件事件,没有找到好的录制工具,和转换gif工具,如果你知道,请给我留言,真是万分感谢。。。嘻嘻请给我免费的那种

在 Android 3.0之前,支持两种动画模式,补间动画 (tween animation)以及 帧动画 (frame animation)。基于前面两种动画的局限性在 Android 3.0中引入了一个新的动画系统 属性动画(property animation),就是今天的主角。

2、相关属性

Property Animation很浅显的就是通过动画的方式改变对象的属性了,我们要先来了解它的几个属性:

属性 含义
Duration 动画的持续时间,默认300ms
Time interpolation 时间插入器 (AccelerateInterpolator DecelerateInterpolator等)
RepeatCount 重复的次数 默认是0
RepeatMode 重复的模式 (正向:ValueAnimator.RESTART 反向:ValueAnimator.REVERSE 默认是 RESTART )
AnimatorSet 用于控制一组动画的执行:比如插入器为线性,执行顺序的先后顺序
AnimatorInflater 用户加载属性动画的xml文件
Animator sets 动画集合,你可以定义一组动画,一起执行或者顺序执行
ObjectAnimator 动画的执行类
ValueAnimator 动画的执行类
TypeEvaluator 类型估值,主要用于设置动画操作属性的值。
PropertyValuesHolder 动画效果类

我们可以通过动画的执行类来设置动画操作对象的属性、执行时间、执行次数、模式,开始和结束的属性值等,然后系统会根据设置的参数来动态改变对象的属性。(对象一般为控件)

我们通过简单的代码来实现动画。

3、ObjectAnimator动画执行类

效果图:

Android Property Animator (属性动画)第一篇_第3张图片

布局文件:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/ivAnim"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_centerInParent="true"
        android:src="@mipmap/face" />

    <Button
        android:id="@+id/bntAnim"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:onClick="bntAnim"
        android:text="属性动画"
        android:textColor="#ffffff" />

RelativeLayout>

Activity代码:

package com.github.ws.animationdemo;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

/**
 * Created by Administrator on 4/5 0005.
 */
public class ObjectAnimatorActivity extends Activity {

    private ImageView ivAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.object_animator);
        ivAnim = (ImageView) findViewById(R.id.ivAnim);
    }

    void bntAnim(View v) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(ivAnim, "rotationY", 0f, 90f);
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.setRepeatCount(2);
        animator.setDuration(1000);
        animator.start();
    }
}

如果你要使用反向animator.setRepeatMode(ValueAnimator.REVERSE);请设置 animator.setRepeatCount(2);值大于0。

对应ObjectAnimator

1、除了ObjectAnimator.ofFloat方法,还有

Android Property Animator (属性动画)第一篇_第4张图片

这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束。如果设置三个,则一个是开始、一个是中间值、一个是结束值~~~

动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~

2、如果你操作对象的该属性方法里面,比如上例的setRotationY如果内部没有调用view的重绘,则你需要自己按照下面方式手动调用。

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                ivAnim.invalidate();// 或者 ivAnim.postInvalidate();
            }
        });

3、上面的例子只有一个属性变化,如果我们想多个属性变化呢?例如:我希望一个view可以缩小,还可以淡出,3个属性变化(scaleX,scaleY,alpha)

效果图:

Android Property Animator (属性动画)第一篇_第5张图片

Activity代码:

    void bntAnim(View v) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(ivAnim, "alpha", 1.0f, 0.0f);
        animator.setDuration(2000);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float ft = (float) valueAnimator.getAnimatedValue(); // 1.0 到 0.0变化
                ivAnim.setScaleX(ft);
                ivAnim.setScaleY(ft);
            }
        });
    }

4、如果我想实现抛物线的效果,水平方向100px/s,垂直方向自由落体,我们一起来看看用ObjectAnimator实现:

效果图:

Android Property Animator (属性动画)第一篇_第6张图片

布局文件:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/ivAnim"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:layout_centerInParent="true"
        android:src="@mipmap/face" />

    <Button
        android:id="@+id/bntAnim"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:onClick="bntAnim"
        android:text="属性动画"
        android:textColor="#ffffff" />

RelativeLayout>

Activity文件:

package com.github.ws.animationdemo;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

/**
 * Created by Administrator on 4/5 0005.
 */
public class PaoActivity extends Activity {

    private ImageView ivPao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pro);
        ivPao = (ImageView) findViewById(R.id.ivFace);
    }

    void bntPao(View v) {
        //注意 pao 属性是我随便起的  主要是为了传值    10.0f 是屏幕的宽度 除以  水平的速度  自己去计算
        ObjectAnimator animator = ObjectAnimator.ofFloat(ivPao, "pao", 0f, 10.0f);
        animator.setDuration(3000);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                // ft  值 为上面传过来的 0.0 到 10.0
                float ft = (float) valueAnimator.getAnimatedValue();
                ivPao.setX(100 * ft);
                ivPao.setY(0.5f * 10 * ft * ft ); //加速度公式
            }
        });

    }
}

5、使用propertyValuesHolder来实现一个动画多个效果:

    public void pvh(View view) {
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
                0f, 1f);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
                0, 1f);
        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
                0, 1f);
        ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(1000).start();
    }

看到这里是不是觉得ObjectAnimator很强大也很简单,使用ObjectAnimator你也可以做出一些很绚丽的效果。

4、ValueAnimator实现动画

和ObjectAnimator用法很类似,我们简单来实现view垂直移动的动画:

    public void verticalMove(View view) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1920);//1080是我手机屏幕的宽度
        animator.setTarget(ivPao);
        animator.setDuration(1000).start();
    }

我们能够很清楚的看到,没有设置操作的属性,运行起来是没有任何效果的。这就是ValueAnimatorObjectAnimator的区别之处,就不需要有getter和setter方法,灵活性就比较高了。你一定会发现我们可以在addUpdateListener来操作属性:

    public void verticalMove() {
        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1920f);//1920是我手机屏幕的高度
        animator.setTarget(ivPao);
        animator.setDuration(2000).start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                ivPao.setTranslationY((float) valueAnimator.getAnimatedValue());
            }
        });
    }

效果图:

Android Property Animator (属性动画)第一篇_第7张图片

下面我们再看来一个例子使用TypeValue来实现抛物线的效果,分析一下,好像只和时间有关系,根据时间的变化,横向和纵向的移动速率是不同的,我们该咋实现呢?此时就要重写TypeValue的时候了,因为我们在时间变化的同时,需要返回给对象两个值,x当前位置,y当前位置。我这里就不贴布局文件了,直接来看代码:

    public void paoMove() {
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(3000);
        valueAnimator.setObjectValues(new PointF(0, 0));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setEvaluator(new TypeEvaluator() {
            @Override
            public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
                //fraction 表示当前的时间变化率   在区间0到1变化
                // fraction = t / duration    t表示当前时间 在区间0到3变化
                PointF point = new PointF();
                //3秒内运动的距离为屏幕的宽度  我屏幕的宽度是1080  所以速度为 1080/3
                point.x = 360 * fraction * 3;
                //这里要计算加速度   (fraction * 3) * (fraction * 3)最大值为9   计算加速度为 42 左右
                point.y = 0.5f * 10 * 42 * (fraction * 3) * (fraction * 3);
                return point;
            }
        });
        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                PointF point = (PointF) valueAnimator.getAnimatedValue();
                ivPao.setX(point.x);
                ivPao.setY(point.y);
            }
        });
    }

我们自定义了TypeValue,每次根据当前每次根据当前时间返回一个PointF对象(PointF和Point的区别就是x,y的单位一个是浮点型float,一个是整形int;的t前 和Rec过intF包含了当前的x,y值,然后通过监听获取x,y值并设置属性。TypeValue根据你具体需求而定

效果图:
Android Property Animator (属性动画)第一篇_第8张图片

5、动画的监听事件

说到监听事件,我们自然会想到Listener,那么动画属性的监听又是怎么样的呢?

    //淡化并删除
    public void deleteView() {
        ObjectAnimator animator = ObjectAnimator.ofFloat(ivPao, "alpha", 1.0f, 0.0f);
        animator.setDuration(2000);
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                //动画开始
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                //动画结束
                ViewGroup parent = (ViewGroup) ivPao.getParent();
                if (parent != null)
                    parent.removeView(ivPao);//是移除了view 并不会占位
            }

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

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

是不是每次我们都需要重写这么多方法呢?我们可以这么做:

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

效果图:

Android Property Animator (属性动画)第一篇_第9张图片

animator还有cancel()和end()方法:cancel动画立即停止;end动画直接到最终状态。

6、AnimatorSet的使用

AnimatorSet是一组动画的集合,还可以设置动画的先后顺序。我们直接看代码:

 public void propertyList() {
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(ivPao, "scaleX", 1.0f, 2f);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(ivPao, "scaleY", 1.0f, 2f);
        ObjectAnimator anim3 = ObjectAnimator.ofFloat(ivPao, "rotationX", 0f, 360f);
        ObjectAnimator anim4 = ObjectAnimator.ofFloat(ivPao, "rotationY", 0f, 360f);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                //super.onAnimationStart(animation);
                ivPao.setPivotX(0);
                ivPao.setPivotY(0);
                ivPao.invalidate();
            }
        });
        animatorSet.play(anim1).with(anim2);
        animatorSet.play(anim3).after(anim2);
        animatorSet.play(anim3).with(anim4);
        animatorSet.start();
    }

注意:after()方法值的是anim3在anim2之后(上面的例子就是先缩放然后翻转)

我们还可以使用playTogether两个动画同时执行,当然还有playSequentially依次执行。animSet.play().with();也是支持链式编程的,但是不要过长,多写点代码。

最后来看看效果图:

Android Property Animator (属性动画)第一篇_第10张图片

由于篇幅原因,Android Property Animator (属性动画)第二篇中我会实现文章开头的动画效果。

源码地址

你可能感兴趣的:(Android)