在上两篇文章主要介绍了 Android 基础动画之帧动画 以及 Android 基础动画之补间动画 。本篇文章主要介绍的是Android基础动画之 属性动画 。
在 补间动画 这篇文章的末尾有说道,补间动画执行完毕以后,加载的view实际上是没有点击事件的,因为点击事件依旧附着在原来的view位置,所以这种动画的完整体验有点蜜汁尴尬。为了解决这种设计之初带来的体验问题,Android3.0以后开始引入属性动画来完美解决这一历史遗留问题。
属性动画的强大之处在于,它可以作用到任何对象(不仅仅针对视图View对象),另外,属性动画还可以自定义各种动画效果(不仅仅是平移、旋转、缩放、透明度的变化)。那么属性动画是如何做到这些功能的?它的原理是在一定时间内,不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
说完了属性动画的作用和优点,下面就用代码去学习掌握属性动画。其中属性动画有两个非常重要的类:分别是ValueAnimator 类、ObjectAnimator 类。
ObjectAnimator:
ObjectAnimator简单理解是直接对对象的属性值进行改变操作,从而实现动画效果。ObjectAnimator本质是通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。ObjectAnimator一般推荐是代码进行使用(下面是基本代码和一些常见的API):
//目标view
TextView mTextView = findViewById(R.id.ob_text);
//平移动画
ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mTextView, "translationX", 200);
// 设置动画运行的时长
translationAnimator.setDuration(500);
// 设置动画延迟播放时间
translationAnimator.setStartDelay(500);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
translationAnimator.setRepeatCount(0);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
translationAnimator.setRepeatMode(ValueAnimator.RESTART);
//开始动画
translationAnimator.start();
//旋转动画
ObjectAnimator rotation = ObjectAnimator.ofFloat(mTextView,"rotation",90);
rotation.setDuration(500);
rotation.start();
//缩放动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(mTextView,"scaleX",1.5f);
scaleX.setDuration(500);
scaleX.start();
//透明度动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(mTextView,"alpha",0.2f);
alpha.setDuration(500);
alpha.start();
你可能会问,那我想通过ObjectAnimator来同时实现多个效果一起运行,那该怎么办?
如果想通过一个ObjectAnimator同时改变多个属性,则需要使用PropertyValuesHolder,参考代码如下:
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX",200);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY",200) ;
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("rotation",90) ;
PropertyValuesHolder holder4 = PropertyValuesHolder.ofFloat("scaleX",1.5f) ;
PropertyValuesHolder holder5 = PropertyValuesHolder.ofFloat("alpha",0.2f) ;
//ObjectAnimator.ofPropertyValuesHolder()
//参数一:目标view
//参数二:可变参数 支持多个PropertyValuesHolder
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, holder1, holder2,holder3,holder4,holder5);
animator.setDuration(500);
animator.start();
下面在来看看ValueAnimator
ValueAnimator
ValueAnimator实现动画的原理是通过不断控制值的变化,然后手动赋给对象的属性,从而实现动画效果。ValueAnimator的大致流程是,先指定将初始值以何种数值(整型、浮点型)的形式 过渡到结束值;接着,开发者手动将值,赋值给目标的属性值。接下来的步伐涉及到了别的内容(插值器与估值器)但还是会慢慢分析。
ValueAnimator重要的方法我基于面向对象的设计原则将其分为两类三个(本质是一类),当然这是我自己的理解。既然是面向对象的设计语言,那么第一步肯定是 new 对象,那么ValueAnimator这个类 new 对象的第一种姿势是这样的:
/**
* ValueAnimator创建对象姿势一:
* 调用ofInt(int...)、ofFloat(float...)
* 形参是可变参数、可传多个参数
* 将传入的多个Int参数进行平滑过渡:假设此处传入0和3,表示将值从0平滑过渡到3
* 以此类推如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,
*/
ValueAnimator animInt = ValueAnimator.ofInt(0, 3);
ValueAnimator animFoat = ValueAnimator.ofFloat(0, 3);
既然拿到了ValueAnimator实例对象以后,接下来就调用对象提供的方法实现具体的功能( 下面是参考代码 )
textView = findViewById(R.id.ob_text);
ValueAnimator animInt = ValueAnimator.ofInt(0, 3);
// 设置动画运行的时长
animInt.setDuration(500);
// 设置动画延迟播放时间
animInt.setStartDelay(500);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
animInt.setRepeatCount(0);
// 设置重复播放动画模式
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
animInt.setRepeatMode(ValueAnimator.RESTART);
animInt.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
float value = (float) animation.getAnimatedValue();
Log.i("app","属性值:"+"fraction:"+fraction+",value:"+value);
//在这里设置具体的动画属性值
textView.setTranslationX(value);
}
});
//开启动画
animInt.start();
说完了第一种new ValueAnimator对象的姿势,现在说第二种:
/**
* ValueAnimator实例化对象姿势二:
* 参数一:TypeEvaluator 估值器
* 参数二:Object... values 可变参数,可以传入具体的动画对象(开始-结束)
*/
ValueAnimator valueAnimator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
return null;
}
},"","");
可以看到,通过ofObject这个函数实例化ValueAnimator对象的时候,需要我们传入一个TypeEvaluator,
以及一个可变参数Object,下面就这2个参数着重说明:
参数一:TypeEvaluator(估值器)
这个就是我们经常提到的估值器。估值器和插值器是很多开发容易搞混淆的一个概念,面试的时候也会问到这个(因为自定义控件会附加动画的内容)本着不抛弃不放弃的精神、下面就对这两个概念进行详尽分析:
首先,这个TypeEvaluator(估值器)本质是一个接口,源码如下:
public interface TypeEvaluator<T> {
/**
* 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.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
其实,通过源码的英文注释就可以分析出这个估值器的实际作用(英文注释翻译过来就是):这个函数返回线性插值起始值和结束值的结果。其中参数fraction代表起始值和结束值之间的比例。简单的计算公式是:result = x0 + t * (x1 - x0);参数的具体是指:x0=startValue;x1=endValue;t=fraction最后,这个方法返回的是,在开始和结束值之间的线性插值,给定分数参数。
简单点说就是,估值器是通过计算公式来进行的值的计算。
还有一点,之前说的ValueAnimator.ofFloat()以及ValueAnimator.ofInt(),这2个方法内部实际上由系统已经设置好了对应的估值器,分别是FloatEvaluator以及IntEvaluator,所以虽然是“两类三个”,但本质上来说还是属于"一类"。
另外既然我们知道了计算公式、那么就可以根据业务定制自己的估值器,下面是参考拓展代码:
/**
* TypeEvaluator里面的泛型可以根据业务去订制
*/
class MyEvaluator implements TypeEvaluator<Object>{
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
/**
* 计算公式自己随意拓展
*/
return null;
}
}
接着将这个自定义的估值器设置到我们的ValueAnimator即可:
ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyEvaluator());
说完了TypeEvaluator我们在来看看插值器:Interpolator:
Interpolator(插值器)
依旧打开源码目睹下庐山真面目:
/**
* 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.
}
嗯,英文注释非常详细,首先是类注释,翻译过来如下:差值器定义了动画的变化速率。这允许基本的动画(透明、缩放、平移、旋转)效果可以加速,减速,重复,等等。关于接口方法内的注释主要是关于新旧版本TimeInterpolator的一些说明。
另外,笔者的Android SDK系统版本是26,所以源码可能会有一些不同。
透过英文注释可以得知:插值器的功能主要是为了丰富(透明、缩放、平移、旋转)这些动画的效果。比如,我们可以通过插值器去设置炫丽的效果,让以往的动画生冷平稳的过渡效果成为历史,让动画变的更立体、更有灵魂。也就是让动画效果变化的模式更客观
系统也为我们提供了一些默认的插值器方便我们使用:
那么,如何使用系统为我们提供好的插值器?声明一个插值器有两种写法:
- 写法一:通过xml文件,对标签的内容进行编写
首先: res/animator的文件夹路径内里创建相应的动画xml
接着:代码编写
<animator xmlns:android="http://schemas.android.com/apk/res/android"
// 初始值
android:valueFrom="0"
// 结束值
android:valueTo="100"
// 变化值类型 :floatType & intType
android:valueType="intType"
// 动画持续时间(ms),必须设置,动画才有效果
android:duration="3000"
// 动画延迟开始时间(ms)
android:startOffset ="1000"
// 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillBefore = “true”
// 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillAfter = “false”
// 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:fillEnabled= “true”
// 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatMode= “restart”
// 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:repeatCount = “0”
// 插值器 这里等价于OvershootInterpolator
android:interpolator = "@android:anim/overshoot_interpolator"
/>
其中,这里的 android:interpolator 标签代表的就是Java中的OvershootInterpolator这个系统为我们写好的插值器
- 写法二:Java代码编写
ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyEvaluator());
Animation animation = new AlphaAnimation(1,0);
//调用setInterpolator 使用插值器
animation.setInterpolator(new OvershootInterpolator());
那系统自带的插值器代表的意思是什么?
Interpolator | 资源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_interpolator | 匀速 |
OvershootInterpolator | @android:anim/overshoot_interpolator | 快速到达终点并超出一小步然后回到终点 |
嗯,以上九个插值器所带来的各种效果是很丰富的(比如,最后一个阶段有弹球的效果;或者先加速再减速),很多博客和资料也是说的是九个。如果你细心看完上面那张系统父子关系层级截图,你会发现系统明明为我们生成了10个默认的插值器,但是你这里只有9个?难道是写漏了一个?嗯,多余的一个是PathInterpolator。
打开PathInterpolator源码可以看到它的两个构造函数,生成的path其实是一个贝赛尔曲线。有兴趣的小伙伴可以自行查阅资料去了解该插值器的使用和说明。
回到正题,一般来说,系统为我们提供的插值器基本上就可以满足开发需求了,如果还是不能满足我们就可以自定义插值器:
class MyInterpolator implements Interpolator{
@Override
public float getInterpolation(float input) {
/**
* 在这里做自己想要做的逻辑
*/
return 0;
}
}
关于自定义插值器里面的getInterpolation(float input)这个方法需要说明一下,这里的input的取值范围是0 - 1,另外,在这个方法内部我们就可以根据业务进行自己想要的计算。
总结:
属性动画是Android3.0之后出现的动画,不仅解决了之前的历史遗留问题,而且通过搭配估值器与插值器的效果让我们的动画更加立体形象、丰富多彩。
如果这篇文章对你有帮助,希望各位看官留下宝贵的star,谢谢。
Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果