(二十)Animator 基础

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、传统的2D动画 — 补间动画+帧动画

在 API3.0 之前,动画使用分为两种:补间动画+帧动画。

补间动画 Tween Animation

补间动画支持平移、旋转、缩放和透明度。与帧动画相比,动画效果已经较为流畅。但是补间动画最大的缺陷是,他只是改变了 View 的显示效果,并不是真正的去改变 View 的位置。举个简单的例子,有个在左边的 Button 按钮,使用补间动画让其平移到右边显示,这时候会发现,点击显示在右边的 Button 是不能触发点击事件的,只有点击原先左边的位置才可以触发点击事件。

帧动画 Frame Animation

帧动画就是轮播一组图片,类似 git 图片的效果。虽然说实现起来简单,但是效果单一,而且需要很多图片,占用资源较大。

帧动画是逐帧进行播放,而补间动画是给出关键两帧,在这两个关键帧之间补充上渐变的效果来实现动画(属性动画也是)。

二、属性动画 Property Animation

属性动画是在 API 3.0以后才出现的,不向下兼容(现在应该基本没有没有 3.0 以下的吧),效果很强大。属性动画是真实修改 View 的实际属性。不过相比补间动画,属性动画的效率会稍微差点。

1.属性动画

先来看一个效果。
这里写图片描述

下面看下这个实现的代码。
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    tools:context="com.xiaoyue.animator.MainActivity">

    <ImageView
        android:id="@+id/duo"
        android:onClick="startAnimator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/duo"/>
</android.support.constraint.ConstraintLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

        imageView = (ImageView) findViewById(R.id.duo);

    }
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i ++) {
                imageView.setTranslationX(3*i);
                try {
                    thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    public void startAnimator(View view){
       thread.start();
    }
}

这边是起一个线程,每隔 30ms 让 imageView 进行平移 3,循环 100次,从而达到动画的效果。属性动画的实现也类似这样,取两个关键帧的时候的属性,在中间过程中均匀变化起属性值,由底层调起,不断进行绘制,达到动画效果,

注:这边没有具体去处理细节,不能多次点击。

2.ObjectAnimator

这里写图片描述

对上面代码修改了 startAnimator 方法。

    public void startAnimator(View view){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);
        objectAnimator.setDuration(500);
        objectAnimator.start();
    }

ofFloat(Object target, String propertyName, float… values)
target : 执行动画的对象
propertyName : 要修改的属性
values : 关键帧的值,即属性变化范围

propertyName 会在 PropertyValuesHolder 中通过反射的机制获取到执行动画对象的 set 方法,然后底层定时调用这个 set 方法。只要是 target 身上有的 set 方法的属性,都可以在这边进行设置。

单独使用 ObjectAnimator 这样的话,只能一次对一个属性进行操作,比如进行平移,进行缩放,却不能同时进行平移和缩放操作。如果说要使用 ObjectAnimator 同时进行多个动画的执行,这时候要设置动画监听,同步操作其他的属性。

3.属性动画的监听

同时放大 X 方向和 Y 方向。

这里写图片描述

    public void startAnimator(View view){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "scaleX", 0f, 1f);
        objectAnimator.setDuration(500);
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float f = (float) animation.getAnimatedValue();
                imageView.setScaleY(f);
            }
        });
        objectAnimator.start();
    }

只修改 startAnimator 方法,在控制 scaleX 的时候,通过监听同时控制 setScaleY 的值。系统会不停的调用监听的方法,getAnimatedValue() 是获取当前属性的值。

这边要强调的是:

ObjectAnimator.ofFloat(imageView, “scaleX”, 0f, 1f);
ObjectAnimator.ofFloat(imageView, “ScaleX”, 0f, 1f);

属性的首写字母大小写是不敏感的,这个在后面源码分析中可以看到原因。但是非首写字母大小写是敏感的。

属性即使反射不到方法,也是不会出错的。下面这段代码效果与上面一样。(Android Studio 检测可能会报错,还是可以打包的。)

    public void startAnimator(View view){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "hehe", 0f, 200f);
        objectAnimator.setDuration(500);
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float f = (float) animation.getAnimatedValue();
                imageView.setScaleX(f/200);
                imageView.setScaleY(f/200);
            }
        });
        objectAnimator.start();
    }

这边是通过 getAnimatedValue() 获取当前设置属性的值,从而计算其他要设置属性的值。其实更多时候是希望直接获取到当前时间的百分比,在 API 12+有提供对应的方法:

getAnimatedFraction(); //返回动画执行的百分比

属性动画的其他监听

        objectAnimator.addListener(new AnimatorListener() {

            @Override
            public void onAnimationStart(Animator animation) {
                //监听动画开始
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //监听动画重复
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //监听动画结束
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //监听动画取消
            }
        });

如果不需要这么多方法,也可以使用 AnimatorListenerAdapter,AnimatorListenerAdapter 是一个实现 AnimatorListener 的抽象类,这样就可以只写自己需要的方法。

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

4.ValueAnimator

ObjectAnimator 是继承与 ValueAnimator,我们实现多动画时候,也可以直接使用 ValueAnimator。
这里写图片描述

    public void startAnimator(View view){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.setDuration(500);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float f = (float) animation.getAnimatedValue();
                imageView.setScaleX(f);
                imageView.setScaleY(f);
            }
        });
        valueAnimator.start();
    }

可以通过初始值的设置,做成一个类似点击效果的动画。
如果只需要监听值变化就用 ValueAnimator,需要监听到属性的变换则使用 ObjectAnimator。

5.PropertyValuesHolder

也是使用 PropertyValuesHolder 创建多个属性动画,然后传递给 ObjectAnimator。

   public void startAnimator(View view){
        PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f);
        PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 0f, 1f);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView,holder1, holder2);
        objectAnimator.setDuration(500);
        objectAnimator.start();
    }

6.AnimatorSet

使用动画集合:
这里写图片描述

    public void startAnimator(View view){
        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView,"scaleX", 0f, 1f);
        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView,"scaleY", 0f, 1f);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(objectAnimator1, objectAnimator2);
        animatorSet.setDuration(500);
        animatorSet.start();
    }

playTogether(Animator… items) 可以是多个动画同时执行,AnimatorSet 还提供了一个方法 playSequentially(Animator… items) 让动画按顺序依次执行。

这里写图片描述

 public void startAnimator(View view){
        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView,"scaleX", 0f, 1f);
        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView,"scaleY", 0f, 1f);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(objectAnimator1, objectAnimator2);
        animatorSet.setDuration(500);
        animatorSet.start();
    }

AnimatorSet 也允许根据自己的需求自由组合动画执行情况。下面三个方法可以混和使用。

//objectAnimator1 与 objectAnimator2 一起执行
animatorSet.play(objectAnimator1).with(objectAnimator2);

//animator1 执行完后执行 animator2
animatorSet.play(objectAnimator1).after(objectAnimator2);

//animator2 执行完后执行 animator1
animatorSet.play(objectAnimator1).before(objectAnimator2);

三、估值器

先来看一个抛物线的效果:

这里写图片描述

看一下用上面方法实现的代码:

    public void startAnimator(View view){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.setDuration(500);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float f = (float) animation.getAnimatedValue();
                //为了方便看,这边扩大100倍。速度为 5。
                imageView.setX(100f*(f*5));
                //加速度 y=vt=1/2*g*t*t
                imageView.setY(100f*0.5f*0.98f*(f*5)*(f*5));
            }
        });
        valueAnimator.start();
    }

在来看一下估值器的使用代码:

    public void startAnimator(View view){
        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(500);
        valueAnimator.setObjectValues(new PointF(0, 0));

        //放在 evaluate 方法里面的话,系统会不断的创建回收,频率较高,会造成内存抖动
        final PointF pointF = new PointF();
        valueAnimator.setEvaluator(new TypeEvaluator<PointF>() {
            @Override
            public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
                pointF.x = 100f*(fraction*5);
                //加速度 y=vt=1/2*g*t*t
                pointF.y = 100f*0.5f*0.98f*(fraction*5)*(fraction*5);
                return pointF;
            }
        });

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF f = (PointF) animation.getAnimatedValue();
                imageView.setX(f.x);
                imageView.setY(f.y);
            }
        });
        valueAnimator.start();
    }

在这个简单的例子里,使用估值器的时候,代码看起来边复杂了。这边只是希望通过对比来学习估值器,当各个变化关系复杂时候,用估值器可以是代码看起来简介。

两个实现都是通过监听某一个值的变化,从而对对应的属性做出变化。ValueAnimator 是可以监听的 Object 变化的,上一段代码是简单的监听一个 float 值,且 ValueAnimator 自身有提供对应的 ofFloat 方法,本质还是调用 ValueAnimator.setObjectValues 方法。

自定义一个估值器,可以在估值器中添加自己的算法,在执行过程中对 Object 的属性进行变化,然后在监听中获取到对应的 Object,然后用对应的值去修改控件的属性。

        //设置一个估值器
        valueAnimator.setEvaluator(new TypeEvaluator<PointF>() {

            @Override
            public PointF evaluate(float fraction, PointF startValue,
                    PointF endValue) {
                // 估值计算方法---可以在执行的过程当中干预改变属性的值---做效果:用自己的算法来控制
                //不断地去计算修改坐标
                pointF.x = 100f*(fraction*5);
                //加速度 y=vt=1/2*g*t*t
//              pointF.y = 0.5f*9.8f*(fraction*5)*(fraction*5);
                pointF.y = 10f*0.5f*9.8f*(fraction*5)*(fraction*5);
                return pointF;
            }
        });

安卓自带实现了一些估值器,也可以直接。

四、插值器

先简单对比一下估计值器和插值器的区别:

估值器:对各个属性计算的算法,具体变化数值
插值器:本质上是一个时间的函数,设置值的变化规律。

插值器默认变化是线性的,值的改变是随时间变换均匀变化的。设置插值器后,则可根据自己的需求让值随时间变换而变化。插值器的实现可以通过改变算法(即估值器)来实现。

AccelerateInterpolator 加速插值器
这里写图片描述

   public void startAnimator(View view){
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationY", 0f,500f);
        objectAnimator.setDuration(800);
        objectAnimator.setInterpolator(new AccelerateInterpolator(1));
        objectAnimator.start();
    }

看一下 AccelerateInterpolator 与时间的关系:可以看见,变化是越来越快。
这里写图片描述

AccelerateDecelerateInterpolator 加速减速插值器
这里写图片描述

   public void startAnimator(View view){
        ObjectAnimator objectAnimator  = ObjectAnimator.ofFloat(imageView, "translationY", 0f,500f);
        objectAnimator.setDuration(800);
        objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        objectAnimator.start();
    }

AccelerateDecelerateInterpolator 是变换速度先快后慢。
这里写图片描述

这些插值器都是安卓自带的,下面在提供一些安卓自带的插值器函数,基本上是够用,不够的也可以自己实现。

AnticipateInterpolator 回荡秋千插值器

这里写图片描述

BounceInterpolator 弹跳插值器

这里写图片描述

CycleInterpolator 正弦周期变化插值器

这里写图片描述

DecelerateInterpolator 减速插值器

这里写图片描述

AnticipateOvershootInterpolator

这里写图片描述

OvershootInterpolator

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值