Android动画(三):Interpolator和Evaluator

本系列将介绍以下内容:
在这里插入图片描述

Android动画

1 插值器Interpolator

1.1 Interpolator概述

有关动画的变化速率的问题是由Interpolator接口决定的,它是用来指定动画如何变化的变量,即用来控制动画的区间值是如何被计算出来的。

哪些动画可以设置插值器呢?
可以:Tween Animation、ValueAnimator、ObjectAnimator、ViewPropertyAnimator
不可以:Frame Animation
即只有逐帧动画不能设置插值器Interpolator。

View Animation中,补间动画的父类Animation中有具体方法setInterpolator(Interpolator i),而逐帧动画的实现类AnimationDrawable没有相关方法,即补间动画可以设置插值器,而逐帧动画不能。
Property Animation均可设置插值器,因为ValueAnimator和ObjectAnimator的父类Animator中有抽象方法setInterpolator(TimeInterpolator value),而ViewPropertyAnimator中单独定义了非抽象方法setInterpolator(TimeInterpolator interpolator)。

Interpolator接口的父接口是TimeInterpolator,而常用的插值器实现类全部继承自抽象类BaseInterpolator。
TimeInterpolator位于包android.animation中,Interpolator、BaseInterpolator及其十个子类全部位于包android.view.animation中。

关于Interpolator接口的UML图(类图Class Diagram)如图所示:
Interpolator类图

Interpolator类图
package android.animation;

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end 当前动画的进度。
     *        该参数与任何设定的值无关,只与时间有关,随着时间的推移,动画的进度也自然增加。
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets. 当前实际想要显示的进度,也即动画的当前数值进度。
     */
    float getInterpolation(float input);
}
package android.view.animation;

import android.animation.TimeInterpolator;

/**
 * An interpolator defines the rate of change of an animation. This allows
 * the basic animation effects (alpha, scale, translate, rotate) to be 
 * accelerated, decelerated, repeated, etc.
 */
public interface Interpolator extends TimeInterpolator {
    // A new interface, TimeInterpolator, was introduced for the new android.animation
    // package. This older Interpolator interface extends TimeInterpolator so that users of
    // the new Animator-based animations can use either the old Interpolator implementations or
    // new classes that implement TimeInterpolator directly.
}
package android.view.animation;

import android.content.pm.ActivityInfo.Config;

/**
 * An abstract class which is extended by default interpolators.
 */
abstract public class BaseInterpolator implements Interpolator {
    private @Config int mChangingConfiguration;
    /**
     * @hide
     */
    public @Config int getChangingConfiguration() {
        return mChangingConfiguration;
    }

    /**
     * @hide
     */
    void setChangingConfiguration(@Config int changingConfiguration) {
        mChangingConfiguration = changingConfiguration;
    }
}

1.2 常用插值器

Interpolator Class
Resource 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_interpolato
OvershootInterpolator
@android:anim/overshoot_interpolator
PathInterpolator
无此用法

常用的Interpolator实现类有十个,全部继承自抽象类BaseInterpolator,且均位于包android.view.animation中:

package android.view.animation;

import android.content.Context;
import android.graphics.animation.HasNativeInterpolator;
import android.graphics.animation.NativeInterpolator;
import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;

/**
 * An interpolator where the rate of change starts and ends slowly but
 * accelerates through the middle.
 */
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolator {
    public AccelerateDecelerateInterpolator() {
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactory.createAccelerateDecelerateInterpolator();
    }
}
package android.view.animation;

@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class AnticipateOvershootInterpolator extends BaseInterpolator
        implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

import android.content.Context;
import android.graphics.animation.HasNativeInterpolator;
import android.graphics.animation.NativeInterpolator;
import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;

/**
 * An interpolator where the rate of change is constant
 */
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactory.createLinearInterpolator();
    }
}
package android.view.animation;

@HasNativeInterpolator
public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
	...
}
package android.view.animation;

@HasNativeInterpolator
public class PathInterpolator extends BaseInterpolator implements NativeInterpolator{
	...
}

1.2.1 在XML中引用插值器

插值器的使用方法有两种,一种是在XML中直接引用,一种是用代码设置。
用法一,在XML文件中直接引用,如

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fillBefore="true"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/linear_interpolator"
    android:toAlpha="0.1">

</alpha>  

1.2.2 通过setInterpolator(xxx)设置插值器

用法二,通过Animation.java中的setInterpolator(xxx)设置,如

alphaAnim.setInterpolator(new LinearInterpolator());

1.3 自定义插值器

从Interpolator的类图结构可以看出,想要自定义插值器只需实现TimeInterpolator接口即可,其用法与普通插值器类似,请浏览以下Demo:

创建一个自定义的Interpolator类

package com.example.InterploatorEvaluator;

import android.animation.TimeInterpolator;


public class MyInterpolator implements TimeInterpolator {

	@Override
    public float getInterpolation(float input) {
        return 1 - input;
    }
    
}

使用时直接调用相关的setInterpolator()即可

xxxAnimation.setInterpolator(new MyInterpolator());

xxxAnimator.setInterpolator(new MyInterpolator());

自定义插值器就这么简单。

2 转换器Evaluator

2.1 Evaluator简介

Evaluator可译为求值程序,在动画中相当于一个转换器,用于将插值器返回的数值(小数)进度转换成对应的数值(位置)。

类似于Interpolator,哪些动画能设置Evaluator呢?
可以:ValueAnimator、ObjectAnimator
不可以:Tween Animation、Frame Animation、ViewPropertyAnimator
即只有常用属性动画ValueAnimator和ObjectAnimator设置转换器Evaluator。

从源码分析,ValueAnimator中有setEvaluator(TypeEvaluator value)方法,而ObjectAnimator继承自ValueAnimator。其余动画类中均没有设置evaluator的方法。
鉴于此,分析Evaluator仍需借助于ValueAniamtor的类图:
ValueAnimator类图

ValueAnimator类图
package android.animation;

public interface TypeEvaluator<T> {
    public T evaluate(float fraction, T startValue, T endValue);
}
package java.lang;

public interface Cloneable {
}
package android.animation;

import java.util.List;


public interface Keyframes extends Cloneable { 

    void setEvaluator(TypeEvaluator evaluator);  
    
    Class getType();  
    Object getValue(float fraction);   
    List<Keyframe> getKeyframes();
    Keyframes clone();
  
    public interface IntKeyframes extends Keyframes {
        int getIntValue(float fraction);
    }
    
    public interface FloatKeyframes extends Keyframes {
        float getFloatValue(float fraction);
    }
}
package android.animation;

public class PropertyValuesHolder implements Cloneable {
	Keyframes mKeyframes = null;
	private TypeEvaluator mEvaluator;

	public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
        mKeyframes.setEvaluator(evaluator);
    }
}
package android.animation;

public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
	/**
     * The property/value sets being animated.
     */
	PropertyValuesHolder[] mValues;
	
	public void setEvaluator(TypeEvaluator value) {
        if (value != null && mValues != null && mValues.length > 0) {
            mValues[0].setEvaluator(value);
        }
    }
}

经源码分析,Evaluator最终由ValueAnimator中的关联关系类PropertyValuesHolder设置。

2.2 Evaluator用法

Evaluator的使用比较简单,直接调用ValueAnimator中的setEvaluator(TypeEvaluator value)方法即可,详见Demo。

布局文件:

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

    <Button
        android:id="@+id/start_anim"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始动画" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_toRightOf="@id/start_anim"
        android:background="#ffff00"
        android:text="看我怎么动"
        android:textSize="18sp" />

</RelativeLayout>

代码示例:

package com.example.InterploatorEvaluator;

import android.animation.IntEvaluator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;


public class InterploatorActivity extends Activity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.interpolater_activity);
        tv = (TextView) findViewById(R.id.tv);

        findViewById(R.id.start_anim).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // 初始化时传入的是ofInt类型的数值
                ValueAnimator animator = ValueAnimator.ofInt(0, 300);

                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int curValue = (Integer) animation.getAnimatedValue();
                        tv.layout(tv.getLeft(), curValue, tv.getRight(), curValue + tv.getHeight());
                    }
                });

                animator.setDuration(1000);
                // 线性变化的插值器
                animator.setInterpolator(new LinearInterpolator());
                // 设置对应的Int类型的Evaluator
                animator.setEvaluator(new IntEvaluator());
                animator.start();
            }
        });
    }
    
}

效果图:
在这里插入图片描述

同时设置了Interpolator和Evaluator

由于Evaluator是把插值器Interpolator返回的小数进度转换成当前数值进度所对应的值。因此如果使用ofInt()方法来定义动画,如ValueAnimator.ofInt(0, 300),那么所对应的Evaluator在返回值时必然返回Integer类型的值,即每种定义方式所对应的Evaluator必然是它专用的(只有定义动画时的数值类型与Evaluator的返回值类型一样,才能使用这个Evaluator)。因为动画数值类型不一样,在通过Evaluator返回时会报强转错误,只有在动画类型一样时,所对应的Evaluator才能通用。

系统有几个默认的Evaluator,如ofInt(xxx)对应IntEvaluator,ofFloat(xxx)对应FloatEvaluator等等,上述Demo中使用的是ValueAnimator.ofInt(0, 300),因此设置Evaluator时是animator.setEvaluator(new IntEvaluator())。

2.3 自定义Evaluator

想要自定义Evaluator只需要分析一下系统内置的IntEvaluator源码即可找到思路:

package android.animation;

/**
 * This evaluator can be used to perform type interpolation between <code>int</code> values.
 */
public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>int</code> or
     *                   <code>Integer</code>
     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

源码很少,可以看到要自定义Evaluator只需要实现泛型接口TypeEvaluator< T >即可,而重要的是evaluate(xxx)中的代码怎么写?

参数分析:
1、fraction参数是插值器中的返回值,表示当前动画的数值进度,以百分制的小数表示。
2、startValue和endValue分别对应ofInt(int start, int end)中的start和end的值。例如定义的动画ofInt(100, 300)进行到数值进度20%的时候,则evaluate(xxx)中的fraction = 0.2,startValue = 100,endValue = 300。
3、evaluate(xxx)的函数返回值就是当前数值进度所对应的具体数值(自定义Evaluator时可自定义计算方法),该值就是在AnimatorUpdateListener监听器中通过animation.getAnimatedValue()得到的数值。

自定义Evaluator的用法参考上文Demo,直接调用setEvaluator(new MyEvaluator())即可,并无区别。

3 Interpolator与Evaluator的区别

分析一下属性动画中ValueAnimator的执行流程:
属性动画执行流程

属性动画执行流程

上图所示流程共四步:
1、ValueAnimator.ofInt(0, 400),指定动画的数值区间,从0到400
2、插值器Interpolator,在动画开始后,通过插值器返回当前动画进度所对应的数值进度。但该数值进度是以小数表示的,如0.2。
3、转换器Evaluator,通过监听器拿到的是当前动画所对应的具体数值,而不是用小数表示的数值。那么必须有一个地方会根据当前的数值进度将其转换成对应的数值,这个地方就是Evaluator。因此Evaluator用于将插值器返回的数值进度转换成对应的数值。
4、监听器返回,通过在ValueAnimator.AnimatorUpdateListener监听器中使用animation.getAnimatedValue()拿到Evaluator中返回的数值。
即,Evaluator就是一个转换器,它能把小数进度转换成对应的数值位置。

解读上述执行流程发现,在插值器Interpolator中,可以通过自定义插值器返回的数值进度来改变返回数值的位置;在转换器Evaluator中,又可以通过改变数值进度所对应的具体数值来改变数值的位置,因此结论就是:
既可以通过重写Interpolator改变数值进度来改变数值位置,也可以通过改变Evaluator中数值进度所对应的具体数值来改变数值位置(插值器只能改变动画进展的快慢,转换器可以改变返回的值)。

参考文献:
[1] UML中的类图及类图之间的关系
[2] 启舰.Android自定义控件开发入门与实战[M].北京:电子工业出版社,2018

微信公众号:TechU
在这里插入图片描述

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值