Android 设计模式 笔记 - 深入了解属性动画
Posted 鲨鱼丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 设计模式 笔记 - 深入了解属性动画相关的知识,希望对你有一定的参考价值。
差不多的开发者都应该知道的android提供的三种属性动画:
- View Animation
- Drawable Animation
- Property Animation
但是在Android系统不断更新完善的过程中,他们添加了很多低版本所没有的属性动画,为了兼容这些低版本的动画,他们创建了一个兼容库,NineOldAnimations。我们就拿NineOldAnimations兼容库来看看属性动画:
属性动画的总体设计:
Animation 通过PropertyValuesHolder 来更新对象的目标属性,如果用户没有设定目标属性的Property对象,那么会通过反射的形式调用目标属性的setter方法来更新属性值;否则,通过Property的set方法来设置属性值,这个属性值则通过KeyFrameSet的计算得到,而KeyFrameSet又是通过时间插值器和类型估值器来计算的,在动画执行的过程中不断计算当前时刻目标属性的值,然后 更新属性值来达到动画效果。
属性动画的核心类介绍:
了解核心类的名称和作用
- ValueAnimation:该类是一个Animation的子类,实现了动画的整个处理逻辑,也是属性动画最核心的类
- ObjectAnimation:对象属性动画的操作类,继承ValueAnimation,通过该类使用动画的形式操作对象的属性
- TimeInterpolator:时间插值器,根据时间流逝的百分比来计算当前属性值改变的百分比,系统预置的有线性插值器(LinearInterpolator),加速减速插值器(AccelerateDecelerateInterpolator)和减速插值器(DecelerateInterpolator)等。
- TypeEvaluator:类型估值器。根据当前属性改变的百分比来计算改变后的属性值,系统预装的有针对整数属性(IntEvaluator),针对浮点属于(FloatEvaluator)和针对Color属性(ArgbEvaluator)
- Property:属性对象,定义了属性的set和get方法
- PropertyValuesHolder:持有目标属性Property、setter和getter方法还有关键帧集合的类。
- KeyFrameSet:存储一个动画的关键帧集合
- AnimationProxy:在Android 3.0以下是使用View的属性动画的辅助类
流程图:
- ValueAnimation流程
- 开始
- 设置动画执行时间,目标对象,属性值
- 启动动画
- 判断是否延迟执行,否的话跳过5、6两步
- 将该动画放到等待队列
- 通过handler发送一个延迟消息来延后执行
- 执行动画
- 根据时间插值器和估值器计算当前属性的值
- 判断是否设置了Property,否的话跳过第10步,是的话跳过第11步
- 通过Property的set方法更新属性值
- 通过反射调用属性的setter方法更新属性值
- 判断东动画是否结束,是的话结束,否的话执行第8步。
- ObJectAnimation流程
- 开始
- 设置动画执行时间,目标对象,属性值
- 启动动画
- 判断动画是否延迟启动,否的话跳过第5、6两步
- 将该动画加入等待队列
- 通过Handler方一个延迟消息来延后执行
- 执行动画
- 根据时间插值器和估值器计算当前时间属性的值
- 判断系统的版本是否小于11,是的话跳过10、否的话跳过11
- 通过Android 3.0 以后的setter方法实现动画方法
- 通过matrix实现动画方法。
- 判断动画是否结束,是的话动画结束,否的话指定第8步
核心原理分析:
我们看下面这行代码,实现一个非常简单的功能,一个View从x轴缩放到原来的0.3倍,动画执行时间为1秒钟:
public void showSimpleAnimation(View view)
ValueAnimator colorAnimation = ObjectAnimator.ofFloat(view,"scaleX",0.3f);
colorAnimation.setDuration(1000) ;
colorAnimation.start();
要看他们的原理我们首先要从ObjectAnimator入手,我们看下ObjectAnimator的ofFloat函数:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
看了代码知道在ObjectAnimator中函数会首先构造一个ObjectAnimator对象,然后根据设置的属性值来初始化各个时间段对应的属性值,这个属性值就是函数里面的values参数,可以看出来values是一个可变参数,如果是一个参数,那么该参数为目标值;如果是两个参数,那么一个是其实值,一个是目标值。我们看下函数setFloatValues的实现:
@Override
public void setFloatValues(float... values)
// PropertyValuesHolder[] mValues;
//mValues 是各个数值的集合
if (mValues == null || mValues.length == 0)
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null)
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
else
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
else
super.setFloatValues(values);
我们首先看到mValues的对象是ObjectAnimator父类ValueAnimator的一个PropertyValuesHolder对象,这个类是该动画库的一个核心类之一。他的作用就是保存属性的名称和setter'和getter方法,以及他的目标值。我们看下这个类的定义:
public class PropertyValuesHolder implements Cloneable
//属性名称
String mPropertyName;
//属性对象
protected Property mProperty;
//属性setter方法
Method mSetter = null;
//属性getter方法
private Method mGetter = null;
//属性值的类型:float,int等
Class mValueType;
//动画关键帧的即可,即在duration时间内的动画帧集合,它保存的是在每个时刻该属性的对应值
KeyframeSet mKeyframeSet = null;
//代码省略
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
//构造的是FloatPropertyValuesHolder对象
return new FloatPropertyValuesHolder(propertyName, values);
//代码省略
//内部类,Float类型的PropertyValuesHolder对象
static class FloatPropertyValuesHolder extends PropertyValuesHolder
//
private FloatProperty mFloatProperty; //float类型属性
FloatKeyframeSet mFloatKeyframeSet; //动画的关键帧
float mFloatAnimatedValue;
//代码省略
public FloatPropertyValuesHolder(Property property, float... values)
super(property);
//设置目标属性值
setFloatValues(values);
if (property instanceof FloatProperty)
mFloatProperty = (FloatProperty) mProperty;
//设置动画的目标值
@Override
public void setFloatValues(float... values)
//调用父类方法
super.setFloatValues(values);
//获取动画的关键帧
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
//计算当前的动画值
@Override
void calculateValue(float fraction)
mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
//代码省略
可以看出这个类是属性和属性值的辅助类,它保存了属性的名称,setter,getter,以及属性在duration时间段内各个时刻对应的属性数值(mKeyframeSet)。动画执行时,动画库只需要根据动画的执行时间,就可以在mKeyframeSet查找到对应的属性值,然后修改执行动画对象的目标属性值,然后连续执行这个过程就可以达到这个动画的效果。上面我们给的例子是属性是scaleX,目标属性值是0.3f。因此,这个例子对应的属性类是FloatPropertyValuesHolder,当然还有IntPropertyValuesHolder等之类的,其实都是一样理解。
在PropertyValuesHolder的内部类FloatPropertyValuesHolder的构造函数调用了setFloatValues来设置动画的目标值。然后才能通过getFloatValue函数获取到动画的关键帧。
这个方法的实现我们看下:
public void setFloatValues(float... values)
mValueType = float.class;
mKeyframeSet = KeyframeSet.ofFloat(values);
可以看到这个方法的调用了 KeyframeSet.ofFloat(values);函数,追踪程序:
public static KeyframeSet ofFloat(float... values)
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1)
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0]))
badValue = true;
else
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i)
keyframes[i] =
(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
if (Float.isNaN(values[i]))
badValue = true;
if (badValue)
Log.w("Animator", "Bad value (NaN) in float animator");
return new FloatKeyframeSet(keyframes);
我们看到关键帧的计算就在ofFloat函数中。如果用户设置了一个目标值,那么这个值就是最终值,他的其实值被默认为0;如果用户设置了大于1的目标值,这些关键帧都会被存储到KetFrameSet对象中。设置完关键帧之后,我们就可以调用start()方法启动动画了,我们去看下OnjectAnimator对象的start()方法:
private void start(boolean playBackwards)
//判断looper是否为空,
if (Looper.myLooper() == null)
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
//设置基本状态
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
mPlayingState = STOPPED;
//启动动画
mStarted = true;
mStartedDelay = false;
mPaused = false;
//将该动画加入到等待执行的动画队列中
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
//判断是否延迟
if (mStartDelay == 0)
setCurrentPlayTime(0);
//设置动画的开始执行时间,因为动画会有一个duration
//这个开始执行时间+duration就是结束时间
mPlayingState = STOPPED;
mRunning = true;
//触发动画监听器
notifyStartListeners();
animationHandler.start();
@Override
public void start()
start(false);
我们看到了,这个函数总共做了几件事:
- 启动动画操作设置了一些基本参数
- 把自己添加到了待执行的动画列表中
- 通过AnimationHandler启动了动画
以上是关于Android 设计模式 笔记 - 深入了解属性动画的主要内容,如果未能解决你的问题,请参考以下文章