Android动画系列:
场景一,当向一个ViewGroup添加控件或者移除控件;场景2,想ListView、GridView或者RecyclerView中添加或者移除数据并更新显示;场景3,通过调用View.setVisibility()设置View的显示或者隐藏。这三种场景虽然能够实现效果,并没有一点过度效果,直来直去的添加或者移除,显得有点生硬。有没有办法添加一定的过度效果,让实现的效果显得圆滑呢?看很多开源框架都已经实现了这些场景的动画效果,动画效果到底是怎么实现的呢?那就不得不说LayoutTransition,接着往下看把。
LayoutTransition类实际上Android系统中的一个实用工具类。使用LayoutTransition类在一个ViewGroup中对布局更改进行动画处理。
以场景1为例,向一个ViewGroup添加控件或者移除控件,这两种效果的过段应对应于控件的显示、控件添加时其他控件的位置移动、控件的消失、控件移除时其他控件的位置移动等四种动画效果。这些动画效果在LayoutTransition中,由以下四个关键字做出了相关声明,
也就是说,我们ViewGroup中有多个Button对象,如果需要删除其中一个Button对象的话,该Button对象可以设置动画(即DISAPPEARING 动画形式),ViewGroup中的其它Button对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_DISAPPEARING 动画形式);若向ViewGroup中添加一个Button,Button对象可以设置动画(即APPEARIN 动画形式),ViewGroup中的其它Button对象此时移动到新的位置的过程中也可以设置相关的动画(即CHANGE_APPEARING 动画形式)。
既然了解到了LayoutTransition的与ViewGroup联动四种动画形式,该如何使用LayoutTransition,老惯例,先了解下其常用的API。
addChild(ViewGroup parent, View child):将child添加至parent
addTransitionListener(LayoutTransition.TransitionListener):添加Layout变化监听
setStartDelay(int transitionType, long delay):设置开始类型transitionType的动画执行的延迟时间
hideChild(ViewGroup parent, View child, int newVisibility):当子视图即将在容器中不可见时,ViewGroup调用此方法。
LayoutTransition.TransitionListener用于监听LayoutTransition的开始和结束,显而意见其有两个回调.
在系统中,为ViewGroup设置了默认的LayoutTransition,只需要在XML中将设置android:animateLayoutChanges=”true”即可,true代表当前Layout的布局发生改变时使用动画效果,false则不使用
XML定义
1.XML定义一个GridLayout
2.核心代码
public class LayoutTransitionActivity extends AppCompatActivity {
private void doAdd() {
Button button = new Button(this);
button.setText("Button- " + mCount);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
button.setLayoutParams(params);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
glContent.removeView(v);
}
});
if (glContent.getChildCount() > 1) {
glContent.addView(button, 1);
} else {
glContent.addView(button);
}
mCount++;
}
private void doRemove() {
if (glContent.getChildCount() > 1) {
glContent.removeView(glContent.getChildAt(1));
} else {
glContent.removeView(glContent.getChildAt(0));
}
}
private void doRemoveAll() {
if (glContent.getChildCount() > 0) {
glContent.removeAllViews();
}
}
// 重新生成LayoutTransition对象并设置给container
private void resetTransition() {
mTransition = new LayoutTransition();
setupCustomAnimations();
glContent.setLayoutTransition(mTransition);
}
// 生成自定义动画
private void setupCustomAnimations() {
// 控制ViewGroup其他自View的移动(除去添加或者移除的子View)
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);
// 动画:CHANGE_APPEARING
// Changing while Adding
PropertyValuesHolder pvhTransX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_TRANSLATION_X,
1f, 0f, 1f);
PropertyValuesHolder pvhPivotX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_X, 0.5f);
PropertyValuesHolder pvhPivotY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_Y, 0.5f);
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_X,1f, 0f,
1f);
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_Y,1f, 0f,
1f);
final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhTransX,pvhScaleX,
pvhScaleY, pvhPivotX, pvhPivotY).setDuration(
mTransition.getDuration(LayoutTransition.CHANGE_APPEARING));
mTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
changeIn.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setScaleX(1f);
view.setScaleY(1f);
}
});
// 动画:CHANGE_DISAPPEARING
// Changing while Removing
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(0.5f, 2f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe(
PropertyConstant.PROPERTY_SCALE_X, kf0, kf1, kf2);
final ObjectAnimator changeOut = ObjectAnimator
.ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight,
pvhBottom, pvhRotation)
.setDuration(
mTransition
.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
mTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
changeOut);
changeOut.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setAlpha(1f);
}
});
// 动画:APPEARING
// view出现时 view自身的动画效果
PropertyValuesHolder appInScaleX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_X, 0f,
1f);
PropertyValuesHolder appInScaleY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_SCALE_Y, 0f,
1f);
PropertyValuesHolder appInPivotX = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_X, 0.5f);
PropertyValuesHolder appInPivotY = PropertyValuesHolder.ofFloat(PropertyConstant.PROPERTY_PIVOT_Y, 0.5f);
ObjectAnimator animIn = ObjectAnimator.ofPropertyValuesHolder(this, appInPivotX, appInPivotY,
appInScaleX, appInScaleY).setDuration(
mTransition.getDuration(LayoutTransition.APPEARING));
mTransition.setAnimator(LayoutTransition.APPEARING, animIn);
animIn.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotationY(0f);
}
});
// 动画:DISAPPEARING
// view 消失时,view自身的动画效果
ObjectAnimator animOut = ObjectAnimator.ofFloat(this, "rotationX", 0f,
90f).setDuration(
mTransition.getDuration(LayoutTransition.DISAPPEARING));
mTransition.setAnimator(LayoutTransition.DISAPPEARING, animOut);
animOut.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setRotationX(0f);
}
});
}
}
LayoutTransition 是API Level 11 才出现的。LayoutTransition的动画效果,只有当ViewGroup中有View添加、删除、隐藏、显示的时候才会体现出来。