LayoutTransition可以实现在ViewGroup组件内部布局结构发生变化的时候,产生一定的动画效果,以免布局切换的时候视觉上很生硬。
首先需要调用
ViewGroup.setLayoutTransition(LayoutTransition)
,这样无论ViewGroup容器添加或移除子视图的时候,都会产生布局变动的动画效果;
如果希望指定自定义的动画效果,使用
LayoutTransition.setAnimator()方法;
布局属性动画一共有五种类型:
APPEARING : 视图出现
DISAPPEARING : 视图消失
CHANGE_APPEARING : 视图出现时,其他组件产生变化
CHANGE_DISAPPEARING : 视图消失时,其他组件产生变化
CHANGING : 非添加和移除组件产生布局变化时,触发的布局
其中前4种是自定义属性动画常用的。
布局属性动画触发条件: 有视图组件被添加进容器或从容器中消失,注:消失是指Gone,如果是Invisible,不会触发布局属性动画;
布局属性动画的执行时序:
DISAPPEARING 动画是立即执行的,
CHANGE_APPEARING ,动画是立即执行的;
APPEARING 动画在CHANGE_APPEARING 完成之后执行;
CHANGE_DISAPPEARING 动画在DISAPPEARING 完成之后执行
添加组件的时候,需要其他组件给待添加组件腾出空间,所以其他组件先移动,空间具备后,待添加的组件执行出现时候的动画效果;移除时顺序反之;
如果希望自己控制这四种动画的执行时间,可以通过LayoutTransition.setDuration(int, long)
和LayoutTransition.setStartDelay(int, long)来手动控制
通过xml布局文件也可以实现简单通用的布局属性动画,
在ViewGroup组件下面设置属性:
animateLayoutChanges="true"
如果布局的嵌套结构比较复杂的话,这种方式可能会失效;官方的解释为复杂布局在布局变化的时候,组件的位置计算可能会出现不一致。
Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the interrelationship of the various levels of layout. Also, a container that is being scrolled at the same time as items are being added or removed is probably not a good candidate for this utility, because the before/after locations calculated by LayoutTransition may not match the actual locations when the animations finish due to the container being scrolled as the animations are running
实例:
/**
* view出现时 绕自身Y轴旋转半周
*/
ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "rotationY", 90F, 0F).
setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);
/**
* view 消失时,绕自身X轴旋转半周
*/
ObjectAnimator animator2 = ObjectAnimator.ofFloat(null, "rotationX", 0F, 90F).
setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animator2);
/**
* view 动画改变时,布局中的每个子view动画的时间间隔
*/
mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
/**
* 为什么这里要这么写?具体我也不清楚,ViewGroup源码里面是这么写的,我只是模仿而已
* 不这么写貌似就没有动画效果了,所以你懂的!
* 参数具体设什么值好像没什么影响,但是在ObjectAnimator中必须要指定这4个属性变化范围
*/
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop =
PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight =
PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom =
PropertyValuesHolder.ofInt("bottom", 0, 1);
/**
* view出现时,导致整个布局改变的动画
* 后面三参数表示,动画从第一个参数变化到第二个参数,最终到3三个参数
*/
PropertyValuesHolder animator3 = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
PropertyValuesHolder animator4 = PropertyValuesHolder.ofFloat("scaleY", 1F, 2F, 1F);
final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom,animator3,animator4).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
changeIn.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1.0f);
}
});
mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
/**
* view消失,导致整个布局改变时的动画
* 帧属性动画,帧代表相对Duration这个周期的相对瞬间,0.5f表示Duration/2时间
*/
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 2f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation =
PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);
final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
changeOut.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1.0f);
}
});
mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
setDuration(int, long)
和LayoutTransition.setStartDelay(int, long)来手动控制
/**
* view出现时 绕自身Y轴旋转半周
*/
ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "rotationY", 90F, 0F).
setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);
/**
* view 消失时,绕自身X轴旋转半周
*/
ObjectAnimator animator2 = ObjectAnimator.ofFloat(null, "rotationX", 0F, 90F).
setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animator2);
/**
* view 动画改变时,布局中的每个子view动画的时间间隔
*/
mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
/**
* 为什么这里要这么写?具体我也不清楚,ViewGroup源码里面是这么写的,我只是模仿而已
* 不这么写貌似就没有动画效果了,所以你懂的!
* 参数具体设什么值好像没什么影响,但是在ObjectAnimator中必须要指定这4个属性变化范围
*/
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop =
PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight =
PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom =
PropertyValuesHolder.ofInt("bottom", 0, 1);
/**
* view出现时,导致整个布局改变的动画
* 后面三参数表示,动画从第一个参数变化到第二个参数,最终到3三个参数
*/
PropertyValuesHolder animator3 = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
PropertyValuesHolder animator4 = PropertyValuesHolder.ofFloat("scaleY", 1F, 2F, 1F);
final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom,animator3,animator4).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
changeIn.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1.0f);
}
});
mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
/**
* view消失,导致整个布局改变时的动画
* 帧属性动画,帧代表相对Duration这个周期的相对瞬间,0.5f表示Duration/2时间
*/
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 2f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation =
PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);
final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
changeOut.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1.0f);
}
});
mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);