目录

 
目录

1. 插值器(Interpolator)

1.1 简介

  • 定义:一个接口
  • 作用:设置 属性值 从初始值过渡到结束值 的变化规律
  1. 如匀速、加速 & 减速 等等
  2. 即确定了 动画效果变化的模式,如匀速变化、加速变化 等等

1.2 应用场景

实现非线性运动的动画效果

非线性运动:动画改变的速率不是一成不变的,如加速 & 减速运动都属于非线性运动

1.3 具体使用

a. 设置方式

插值器在动画的使用有两种方式:在XML / Java代码中设置:

设置方法1:在 动画效果的XML代码中设置插值器属性android:interpolator

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/overshoot_interpolator"
// 通过资源ID设置插值器
android:duration="3000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2" />

设置方法2:在 Java 代码中设置

Button mButton = (Button) findViewById(R.id.Button);
// 步骤1:创建 需要设置动画的 视图View Animation alphaAnimation = new AlphaAnimation(1,0);
// 步骤2:创建透明度动画的对象 & 设置动画效果 alphaAnimation.setDuration(3000);
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步骤3:创建对应的插值器类对象 alphaAnimation.setInterpolator(overshootInterpolator);
// 步骤4:给动画设置插值器 mButton.startAnimation(alphaAnimation);
// 步骤5:播放动画
  • 那么使用插值器时的资源ID是什么呢?即有哪些类型的插值器可供我们使用呢?
  • 下面将介绍 Android内置默认的插值器

b. 系统内置插值器类型

  • Android内置了 9 种内置的插值器实现:
作用 资源ID 对应的Java类
动画加速进行 @android:anim/accelerate_interpolator AccelerateInterpolator
快速完成动画,超出再回到结束样式 @android:anim/overshoot_interpolator OvershootInterpolator
先加速再减速 @android:anim/accelerate_decelerate_interpolator AccelerateDecelerateInterpolator
先退后再加速前进 @android:anim/anticipate_interpolator AnticipateInterpolator
先退后再加速前进,超出终点后再回终点 @android:anim/anticipate_overshoot_interpolator AnticipateOvershootInterpolator
最后阶段弹球效果 @android:anim/bounce_interpolator BounceInterpolator
周期运动 @android:anim/cycle_interpolator CycleInterpolator
减速 @android:anim/decelerate_interpolator DecelerateInterpolator
匀速 @android:anim/linear_interpolator LinearInterpolator

使用时:

  • 当在XML文件设置插值器时,只需传入对应的插值器资源ID即可
  • 当在Java代码设置插值器时,只需创建对应的插值器对象即可

系统默认的插值器是AccelerateDecelerateInterpolator,即先加速后减速

  • 系统内置插值器的效果图:

     
    效果图
  • 使用Android内置的插值器能满足大多数的动画需求

  • 如果上述9个插值器无法满足需求,还可以自定义插值器

  • 下面将介绍如何自定义插值器(Interpolator)

c. 自定义插值器

  • 本质:根据动画的进度(0%-100%)计算出当前属性值改变的百分比
  • 具体使用:自定义插值器需要实现 Interpolator / TimeInterpolator接口 & 复写getInterpolation()
  1. 补间动画 实现 Interpolator接口;属性动画实现TimeInterpolator接口
  2. TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用
// Interpolator接口
public interface Interpolator { // 内部只有一个方法
float getInterpolation(float input) {
// 参数说明
// input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
// 即动画开始时,input值 = 0;动画结束时input = 1
// 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加 ...// 插值器的计算逻辑 return xxx;
// 返回的值就是用于估值器继续计算的fraction值,下面会详细说明
} // TimeInterpolator接口
// 同上
public interface TimeInterpolator { float getInterpolation(float input); }

在学习自定义插值器前,我们先来看两个已经实现好的系统内置差值器:

  • 匀速插值器:LinearInterpolator
  • 先加速再减速 插值器:AccelerateDecelerateInterpolator
// 匀速差值器:LinearInterpolator
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
// 仅贴出关键代码
...
public float getInterpolation(float input) {
return input;
// 没有对input值进行任何逻辑处理,直接返回
// 即input值 = fraction值
// 因为input值是匀速增加的,因此fraction值也是匀速增加的,所以动画的运动情况也是匀速的,所以是匀速插值器
} // 先加速再减速 差值器:AccelerateDecelerateInterpolator
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
// 仅贴出关键代码
...
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
// input的运算逻辑如下:
// 使用了余弦函数,因input的取值范围是0到1,那么cos函数中的取值范围就是π到2π。
// 而cos(π)的结果是-1,cos(2π)的结果是1
// 所以该值除以2加上0.5后,getInterpolation()方法最终返回的结果值还是在0到1之间。只不过经过了余弦运算之后,最终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程
// 所以最终,fraction值 = 运算后的值 = 先加速后减速
// 所以该差值器是先加速再减速的
} }
  • 从上面看出,自定义插值器的关键在于:对input值 根据动画的进度(0%-100%)通过逻辑计算 计算出当前属性值改变的百分比
  • 下面我将用一个实例来说明该如何自定义插值器

实例

  • 目的:写一个自定义Interpolator:先减速后加速

步骤1:根据需求实现Interpolator接口
DecelerateAccelerateInterpolator.java

/**
* Created by Carson_Ho on 17/4/19.
*/ public class DecelerateAccelerateInterpolator implements TimeInterpolator { @Override
public float getInterpolation(float input) {
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
// 使用正弦函数来实现先减速后加速的功能,逻辑如下:
// 因为正弦函数初始弧度变化值非常大,刚好和余弦函数是相反的
// 随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。
// 当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
return result;
// 返回的result值 = 随着动画进度呈先减速后加速的变化趋势
}

}

MainActivity.java

 mButton = (Button) findViewById(R.id.Button);
// 创建动画作用对象:此处以Button为例 float curTranslationX = mButton.getTranslationX();
// 获得当前按钮的位置 ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
// 创建动画对象 & 设置动画
// 表示的是:
// 动画作用对象是mButton
// 动画作用的对象的属性是X轴平移
// 动画效果是:从当前位置平移到 x=1500 再平移到初始位置
animator.setDuration(5000);
animator.setInterpolator(new DecelerateAccelerateInterpolator());
// 设置插值器
animator.start();
// 启动动画

效果图

 
差值器.gif

2. 估值器(TypeEvaluator)

2.1 简介

  • 定义:一个接口
  • 作用:设置 属性值 从初始值过渡到结束值 的变化具体数值
  1. 插值器(Interpolator)决定 值 的变化规律(匀速、加速blabla),即决定的是变化趋势;而接下来的具体变化数值则交给
    而估值器
  2. 属性动画特有的属性

2.2 应用场景

协助插值器 实现非线性运动的动画效果

非线性运动:动画改变的速率不是一成不变的,如加速 & 减速运动都属于非线性运动

2.3 具体使用

a. 设置方式

ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);
// 在第4个参数中传入对应估值器类的对象
// 系统内置的估值器有3个:
// IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
// FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
// ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡

效果图

 
FloatEvaluator
 
IntEvaluator
  • 如果上述内置的估值器无法满足需求,还可以自定义估值器
    下面将介绍如何自定义插值器(Interpolator)

b. 自定义估值器

  • 本质:根据 插值器计算出当前属性值改变的百分比 & 初始值 & 结束值 来计算 当前属性具体的数值

如:动画进行了50%(初始值=100,结束值=200 ),那么匀速插值器计算出了当前属性值改变的百分比是50%,那么估值器则负责计算当前属性值 = 100 + (200-100)x50% = 150.

  • 具体使用:自定义估值器需要实现 TypeEvaluator接口 & 复写evaluate()
public interface TypeEvaluator {  

    public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值 ....// 估值器的计算逻辑 return xxx;
// 赋给动画属性的具体数值
// 使用反射机制改变属性变化 // 特别注意
// 那么插值器的input值 和 估值器fraction有什么关系呢?
// 答:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
}
}

在学习自定义插值器前,我们先来看一个已经实现好的系统内置差值器:浮点型插值器:FloatEvaluator

public class FloatEvaluator implements TypeEvaluator {
// FloatEvaluator实现了TypeEvaluator接口 // 重写evaluate()
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
// 初始值 过渡 到结束值 的算法是:
// 1. 用结束值减去初始值,算出它们之间的差值
// 2. 用上述差值乘以fraction系数
// 3. 再加上初始值,就得到当前动画的值
}
}
  • 属性动画中的ValueAnimator.ofInt() & ValueAnimator.ofFloat()都具备系统内置的估值器,即FloatEvaluator & IntEvaluator

即系统已经默认实现了 如何从初始值 过渡到 结束值 的逻辑

  • 但对于ValueAnimator.ofObject(),从上面的工作原理可以看出并没有系统默认实现,因为对对象的动画操作复杂 & 多样,系统无法知道如何从初始对象过度到结束对象
  • 因此,对于ValueAnimator.ofObject(),我们需自定义估值器(TypeEvaluator)来告知系统如何进行从 初始对象 过渡到 结束对象的逻辑
  • 自定义实现的逻辑如下
// 实现TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator{ // 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值 ... // 写入对象动画过渡的逻辑 return value;
// 返回对象动画过渡的逻辑计算后的值
}

实例说明

  • 下面我将用实例说明 该如何自定义TypeEvaluator接口并通过ValueAnimator.ofObject()实现动画效果

  • 实现的动画效果:一个圆从一个点 移动到 另外一个点

     
    效果图
  • 工程目录文件如下:

     
    工程目录

步骤1:定义对象类

  • 因为ValueAnimator.ofObject()是面向对象操作的,所以需要自定义对象类。
  • 本例需要操作的对象是 圆的点坐标
    Point.java
public class Point {

    // 设置两个变量用于记录坐标的位置
private float x;
private float y; // 构造方法用于设置坐标
public Point(float x, float y) {
this.x = x;
this.y = y;
} // get方法用于获取坐标
public float getX() {
return x;
} public float getY() {
return y;
}
}

步骤2:根据需求实现TypeEvaluator接口

  • 实现TypeEvaluator接口的目的是自定义如何 从初始点坐标 过渡 到结束点坐标;
  • 本例实现的是一个从左上角到右下角的坐标过渡逻辑。

     
    效果图

PointEvaluator.java

// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator { // 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) { // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue; // 根据fraction来计算当前动画的x和y的值
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY()); // 将计算后的坐标封装到一个新的Point对象中并返回
Point point = new Point(x, y);
return point;
} }
  • 上面步骤是根据需求自定义TypeEvaluator的实现
  • 下面将讲解如何通过对 Point 对象进行动画操作,从而实现整个自定义View的动画效果。

步骤3:将属性动画作用到自定义View当中

MyView.java

/**
* Created by Carson_Ho on 17/4/18.
*/
public class MyView extends View {
// 设置需要用到的变量
public static final float RADIUS = 70f;// 圆的半径 = 70
private Point currentPoint;// 当前点坐标
private Paint mPaint;// 绘图画笔 // 构造方法(初始化画笔)
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
} // 复写onDraw()从而实现绘制逻辑
// 绘制逻辑:先在初始点画圆,通过监听当前坐标值(currentPoint)的变化,每次变化都调用onDraw()重新绘制圆,从而实现圆的平移动画效果
@Override
protected void onDraw(Canvas canvas) {
// 如果当前点坐标为空(即第一次)
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
// 创建一个点对象(坐标是(70,70)) // 在该点画一个圆:圆心 = (70,70),半径 = 70
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint); // (重点关注)将属性动画作用到View中
// 步骤1:创建初始动画时的对象点 & 结束动画时的对象点
Point startPoint = new Point(RADIUS, RADIUS);// 初始点为圆心(70,70)
Point endPoint = new Point(700, 1000);// 结束点为(700,1000) // 步骤2:创建动画对象 & 设置初始值 和 结束值
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
// 参数说明
// 参数1:TypeEvaluator 类型参数 - 使用自定义的PointEvaluator(实现了TypeEvaluator接口)
// 参数2:初始动画的对象点
// 参数3:结束动画的对象点 // 步骤3:设置动画参数
anim.setDuration(5000);
// 设置动画时长 // 步骤3:通过 值 的更新监听器,将改变的对象手动赋值给当前对象
// 此处是将 改变后的坐标值对象 赋给 当前的坐标值对象
// 设置 值的更新监听器
// 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
// 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
// 从而更新当前坐标值(currentPoint) // 步骤4:每次赋值后就重新绘制,从而实现动画效果
invalidate();
// 调用invalidate()后,就会刷新View,即才能看到重新绘制的界面,即onDraw()会被重新调用一次
// 所以坐标值每改变一次,就会调用onDraw()一次
}
}); anim.start();
// 启动动画 } else {
// 如果坐标值不为0,则画圆
// 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果 // 在该点画一个圆:圆心 = (30,30),半径 = 30
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
} }

步骤4:在布局文件加入自定义View空间

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="scut.carson_ho.valueanimator_ofobject.MainActivity"> <scut.carson_ho.valueanimator_ofobject.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>

步骤5:在主代码文件设置显示视图

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

效果图

 
效果图

源码地址

Carson_Ho的Github地址


3. 总结

  • 本文对Android 动画中的 插值器和估值器的使用 进行了详细分析,相信通过本文你已经能实现复杂的动画效果

作者:Carson_Ho
链接:https://www.jianshu.com/p/2f19fe1e3ca1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android 动画:你真的会使用插值器与估值器吗?的更多相关文章

  1. Android属性动画:插值器与估值器

    声明:本篇文章部分内容来自<Android开发艺术探索>. 我们都知道对于属性动画可以对某个属性做动画,而 插值器(TimeInterpolator)和 估值器(TypeEvaluator ...

  2. 《Android开发艺术探索》读书笔记 (7) 第7章 Android动画深入分析

    本节和<Android群英传>中的第七章Android动画机制与使用技巧有关系,建议先阅读该章的总结 第7章 Android动画深入分析 7.1 View动画 (1)android动画分为 ...

  3. # Android动画笔记

    标签: Android开发艺术探索笔记 View动画 帧动画 属性动画 View动画 View动画的作用对象时View,有4种动画效果,分别是平移动画.缩放动画.旋转动画.和透明度动画. 此类动画通常 ...

  4. 深入分析Android动画(一)

    动画的分类: ①View动画 View动画顾名思义其作用对象为View,包含平移.缩放.旋转.透明,这四类变化分别对应着Animation的子类TranlateAnimation.ScaleAnima ...

  5. Android动画(二)-属性动画

    我们在上一篇博客中,讨论了视图动画与帧动画.那么这节课则要讨论更复杂,更强大的Property animation(属性动画). 视图动画使用简单,但是功能也简单.(只有那四种功能).并且也不改变Vi ...

  6. Android动画深入分析

    动画分类 Android动画可以分3种:View动画,帧动画和属性动画:属性动画为API11的新特性,在低版本是无法直接使用属性动画的,但可以用nineoldAndroids来实现(但是本质还是vii ...

  7. Android动画系列之属性动画

    原文首发于微信公众号:jzman-blog,欢迎关注交流! 属性动画相较帧动画和补间动画更强大,帧动画和补间动画只能应用于 View 及其子类,而属性动画可以修改任何对象的属性值,属性值可在指定的一段 ...

  8. 第七章:Android动画深入分析

    7.1 View动画 View动画的作用对象是View,它支持四种动画效果,分别是平移动画,缩放动画,旋转动画和透明动画. 帧动画也属于View动画,但是帧动画的表现形式和上面的四种变换效果不太一样. ...

  9. Android动画效果之Property Animation进阶(属性动画)

    前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...

随机推荐

  1. ReactNative系列组件用法(一)

    首先我们来认识view 改变一些特性,再来看看项目的变化 我们新增flex布局的一些属性,再来看看项目的变化 接下来我们来看看如果获取屏幕的分辨率 关于图片的用法,reactNative这里也是很神奇 ...

  2. 【更新】搭建 Zookeeper-3.4.11 集群

    先准备好三台linux(虚拟机). 1. 先把Java环境配好.我CentOS-7-x86_64-DVD-1708 + jdk1.8.0_161 1.1 先把jdk上传到系统里面(我利用的Filezi ...

  3. Tarjan 模板,高级并查集

    第一个模板有误!!!! 请见谅!!! 要怪就怪HDU吧,竟然让我过了 第二个模板是正确的.请翻到下面看更新 HDU 1269 评论区居然有人说用并查集过了,其实回想一下 求无向图的连通分量,就是并查集 ...

  4. 清除ul li里面的浮动并让ul自适应高度的一个好办法

    有时候会遇到ul li列表里面的东西会用到浮动,这个时候ul的高度就不会被撑开,这怎么办呢? 1)最笨的方法就是设置ul的高度,但这种方法很死板,高度不能自适应 2)有次我试着在ul里面加一个清除浮动 ...

  5. django分页功能

    采用django自带的Paginator功能 from django.core.paginator import Paginator food = foodInfo.objects.filter(fo ...

  6. ajax上传文件及进度显示

    之前在博文:原生ajax写法就提及过ajax2.0与1.0的差别是多了FormData和利用FormData文件上传(当然还有跨域,但不是本文的重点). 那么具体怎么样实现ajax上传文件呢? 一般来 ...

  7. ruby pluck用法,可以快速从数据库获取 对象的 指定字段的集合数组

    可以快速从数据库获取 对象的 指定字段的集合数组 比如有一个users表,要等到user的id数组: select id from users where age > 20; 要实现在如上sql ...

  8. cdh-5.10.0搭建安装

    1.修改主机名为master, slave1, slave2 vim /etc/sysconfig/network HOSTNAME = master HOSTNAME = slave1 HOSTNA ...

  9. LR(0)文法项目集规范族、DFA和分析表的构建实例

    最近在复习编译原理,考试之前以为自己懂了,眼高手低就没去实践.结果一考试出问题了.... 学习就要脚踏实地,容不得半点模糊.凭着侥幸心理很危险的.以后要引以为戒啊. 特别写出这篇文章 :一来总结一下这 ...

  10. qemu基本使用

    1.qemu的安装 请参考家用路由器0day漏洞挖掘技术这本书 2.基本使用 qemu有主要如下两种运作模式: 用户模式(User Mode),亦称使用者模式.qemu能启动那些为不同中央处理器编译的 ...