一、概述 |
LayoutAnimationController用于一个布局或者ViewGroup的动画控制器,它能够让每个子View在不同的时间点分别执行相同的动画,ViewGroup通过LayoutAnimationController计算每个子view的动画开始的时间间隔,这个时间的间隔是通过某种规律特点进行计算,比如它在viewGroup中的index顺序。
间隔时间的计算是通过view在viewgroup的index顺序乘以单位时间间隔计算得出。它的子类应该重写:
- getDelayForView(android.view.View):实现一种不同的计算delay的延迟时间方式。例如子类GridLayoutAnimationController,它基于child view的column和row值来计算延迟。
用于计算每个子view的动画延迟信息被存储在LayoutAnimationController.AnimationParameters中。XML文件配置通常有一下几个参数:
- LayoutAnimation_delay:延迟时间
- LayoutAnimation_animationOrder:动画播放顺序
- LayoutAnimation_interpolator:动画的差值,有加速,减速等
- LayoutAnimation_animation:动画的名称
二、动画播放顺序 |
LayoutAnimationController类中对应的播放顺序:
- public static final int ORDER_NORMAL = 0:按照view添加到view group中的顺序进行播放动画展示
- public static final int ORDER_REVERSE = 1;按照view添加到view group中倒叙进行播放动画展示。
- public static final int ORDER_RANDOM = 2;随机顺序播放view的动画。
三、源代码简要分析 |
我们看下另外几个成员变量:
public class LayoutAnimationController {
public static final int ORDER_NORMAL = 0;
public static final int ORDER_REVERSE = 1;
public static final int ORDER_RANDOM = 2;
//view展示的Animation动画
protected Animation mAnimation;
//当播放顺序为随机的时候,用于计算播放顺序
protected Random mRandomizer;
//Animation动画的插值器
protected Interpolator mInterpolator;
//动画的间隔时间
private float mDelay;
//动画的播放顺序
private int mOrder;
//动画的持续时间
private long mDuration;
//最大的间隔时间
private long mMaxDelay;
****
}
我们如何构建一个LayoutAnimationController呢?看下它的构造函数。
- public LayoutAnimationController(Context context, AttributeSet attrs)
- public LayoutAnimationController(Animation animation)
- public LayoutAnimationController(Animation animation, float delay)
我们看下构造函数的实现:
1、
public LayoutAnimationController(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);
Animation.Description d = Animation.Description.parseValue(
a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
mDelay = d.value;
mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);
int resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_animation, 0);
if (resource > 0) {
setAnimation(context, resource);
}
resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_interpolator, 0);
if (resource > 0) {
setInterpolator(context, resource);
}
a.recycle();
}
通过上面的代码发现,构造函数中就是类似于我们的自定义View使用,获取attrs中所定义的属性值,然后赋值到对应的成员变量上。
2、
public LayoutAnimationController(Animation animation) {
this(animation, 0.5f);
}
public LayoutAnimationController(Animation animation, float delay) {
mDelay = delay;
setAnimation(animation);
}
这两个构造函数中,传入Animation动画,同时设置的默认事件,延迟50%。
LayoutAnimationController中给我们提供了很多函数,同时提供了一些设置成员变量值的函数。我们挑一些来介绍:
1、判断两个view之间的动画是否具有重叠,重叠返回true,不重叠返回false
public boolean willOverlap() {
return mDelay < 1.0f;
}
2、启动动画:
public void start() {
mDuration = mAnimation.getDuration();
mMaxDelay = Long.MIN_VALUE;
mAnimation.setStartTime(-1);
}
3、获取应用在view上的Animation动画对象:
public final Animation getAnimationForView(View view) {
final long delay = getDelayForView(view) + mAnimation.getStartOffset();
mMaxDelay = Math.max(mMaxDelay, delay);
try {
final Animation animation = mAnimation.clone();
animation.setStartOffset(delay);
return animation;
} catch (CloneNotSupportedException e) {
return null;
}
}
4、判断动画是否完成:所有子view完成动画返回true,否则返回false
public boolean isDone() {
return AnimationUtils.currentAnimationTimeMillis() >
mAnimation.getStartTime() + mMaxDelay + mDuration;
}
5、获取view的动画延迟数:
/**
* 返回view的动画延迟毫秒数,动画一定会延迟,子类需要重写该方法,来
* 计算返回一个数
* child animation delay = child index * delay
*
* index序列从ViewGroup.LayoutParams的AnimationParameters中获取
*/
protected long getDelayForView(View view) {
//获取view的LayoutParams
ViewGroup.LayoutParams lp = view.getLayoutParams();
//在LayoutParams属性中获取view的AnimationParameters
AnimationParameters params = lp.layoutAnimationParameters;
//判断AnimationParameters是否为空
if (params == null) {
return 0;
}
//计算动画的延迟时间delay * duration
final float delay = mDelay * mAnimation.getDuration();
//计算view的延迟时间index * delay
final long viewDelay = (long) (getTransformedIndex(params) * delay);
//view的全部延迟时间delay * count
final float totalDelay = delay * params.count;
//动画的插值器是否为空
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
float normalizedDelay = viewDelay / totalDelay;
normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
return (long) (normalizedDelay * totalDelay);
}
6、获取变换的index顺序
/**
* 变换的index值存储在AnimationParameters对象中
* 子类需要重写这个方法来提供额外的不同顺序。
* 该方法被getDelayForView(android.view.View)调用
*/
protected int getTransformedIndex(AnimationParameters params) {
switch (getOrder()) {
case ORDER_REVERSE:
return params.count - 1 - params.index;
case ORDER_RANDOM:
if (mRandomizer == null) {
mRandomizer = new Random();
}
return (int) (params.count * mRandomizer.nextFloat());
case ORDER_NORMAL:
default:
return params.index;
}
}
主要的几个方法介绍完了,我们看下AnimationParameters这个内部类。
public static class AnimationParameters {
//view group中的view个数
public int count;
//即将展示view的index序列
public int index;
}
针对上面的几个主要方法的源码分析,我们知道如果我们自定义一个LayoutAnimationController,需要我们控制顺序,控制顺序就需要我们重写getTransformedIndex()方法,我们通过AnimationParameters类中的属性进行index的计算。然后由getDelayForView()方法计算每个view的延迟时间。
四、GridLayoutAnimationController案例分析 |
仔细看了上面的介绍,我们粗略的说了GridLayoutAnimationController是通过row和column两个值来进行计算各个view之间的延迟时间。一般我们自定义LayoutAnimationController的时候需要我们重写getTransformedIndex()方法来确定我们的view展现的顺序,然后getDelayForView()函数进行计算delay的时间,如有需要我们也可以重写。所以针对分析,我们仅仅看看GridLayoutAnimationController中的这两个方法的实现,其它的方法基本与LayoutAnimationController的类似。
先来观摩下getDelayForView()的源码吧!
protected long getDelayForView(View view) {
//获取LayoutParams
ViewGroup.LayoutParams lp = view.getLayoutParams();
//获取AnimationParameters
AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;
if (params == null) {
return 0;
}
//获取column的index
final int column = getTransformedColumnIndex(params);
//获取row的index
final int row = getTransformedRowIndex(params);
//获取column的个数和row的个数
final int rowsCount = params.rowsCount;
final int columnsCount = params.columnsCount;
//计算列、行的延迟数
final long duration = mAnimation.getDuration();
final float columnDelay = mColumnDelay * duration;
final float rowDelay = mRowDelay * duration;
float totalDelay;
long viewDelay;
if (mInterpolator == null) {
mInterpolator = new LinearInterpolator();
}
//判断动画的展示方向
switch (mDirectionPriority) {
case PRIORITY_COLUMN://列优先
viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay);
totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay;
break;
case PRIORITY_ROW://行优先
viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay);
totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay;
break;
case PRIORITY_NONE:
default:
viewDelay = (long) (column * columnDelay + row * rowDelay);
totalDelay = columnsCount * columnDelay + rowsCount * rowDelay;
break;
}
//计算延迟时间
float normalizedDelay = viewDelay / totalDelay;
normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
return (long) (normalizedDelay * totalDelay);
}
上面的注释很清晰了,代码也不是很难,在上面代码中通过getTransformedColumnIndex()、getTransformedRowIndex()方法来获取列和行的index值。我们看下这两个函数的源代码。
private int getTransformedColumnIndex(AnimationParameters params) {
int index;
switch (getOrder()) {//获取动画执行的顺序
case ORDER_REVERSE:
index = params.columnsCount - 1 - params.column;
break;
case ORDER_RANDOM:
if (mRandomizer == null) {
mRandomizer = new Random();
}
index = (int) (params.columnsCount * mRandomizer.nextFloat());
break;
case ORDER_NORMAL:
default:
index = params.column;
break;
}
int direction = mDirection & DIRECTION_HORIZONTAL_MASK;
if (direction == DIRECTION_RIGHT_TO_LEFT) {
index = params.columnsCount - 1 - index;
}
return index;
}
private int getTransformedRowIndex(AnimationParameters params) {
int index;
switch (getOrder()) {
case ORDER_REVERSE:
index = params.rowsCount - 1 - params.row;
break;
case ORDER_RANDOM:
if (mRandomizer == null) {
mRandomizer = new Random();
}
index = (int) (params.rowsCount * mRandomizer.nextFloat());
break;
case ORDER_NORMAL:
default:
index = params.row;
break;
}
int direction = mDirection & DIRECTION_VERTICAL_MASK;
if (direction == DIRECTION_BOTTOM_TO_TOP) {
index = params.rowsCount - 1 - index;
}
return index;
}
这里面也是计算index,同时又定义了一个新的AnimationParameters内部类。
public static class AnimationParameters extends
LayoutAnimationController.AnimationParameters {
/**
* The view group's column to which the view belongs.
*/
public int column;
/**
* The view group's row to which the view belongs.
*/
public int row;
/**
* The number of columns in the view's enclosing grid layout.
*/
public int columnsCount;
/**
* The number of rows in the view's enclosing grid layout.
*/
public int rowsCount;
}
基本的实现就是这个样子。在以后的开发中我们也可以结合实际的项目需要,进行LayoutAnimationController的自定义实现。下一篇博客中,实现几个案例来进行练手使用。LayoutAnimationController实战案例