5.1 问题
应用程序动态地添加或移除布局中的视图,希望这种变化能够以动画的形式展示出来。
5.2 解决方案
(API Level 11)
使用LayoutTransition对象自定义在布局中对视图结构修改后的动画效果。在Android3.0以后的版本中,只需要简单地在XML中设置android:animateLayoutChanges标识或者在Java代码中添加一个LayoutTransition对象,即可实现任何ViewGroup改变布局时的动画效果。
布局中的每个View对象在布局变换时有5种状态。应用程序可以为下面任何一种状态设置自定义动画:
- APPEARING:容器中出现一个视图。
- DISAPPEARING:容器中消失一个视图。
- CHANGING:布局改变导致某个视图随之改变,例如调整大小,但不包括添加或移除视图。
- CHANGE_APPEARING:其他视图的出现导致某个视图改变。
- CHANGE_DISAPPEARING:其他视图的消失导致某个视图改变。
5.3 实现机制
以下两个代码演示了一个应用程序,该应用程序在基本的LinearLayout改变时创建动画。
res/layout/main.xml
添加或移除视图的Activity
public class MainActivity extends Activity {
LinearLayout mContainer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mContainer = (LinearLayout)findViewById(R.id.verticalContainer);
}
//添加可以移除自身的视图
public void onAddClick(View v) {
Button button = new Button(this);
button.setText("Click To Remove");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mContainer.removeView(v);
}
});
mContainer.addView(button, new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
}
这个简单的示例在单击Add Item按钮时会在LinearLayout中添加Button实例。每个新的按钮在单击时都具有将自己从布局中移除的功能。要使此过程动起来,我们只需要在LinearLayout上设置android:animateLayoutChanges = "true",之后框架会进行接下来的动作。默认情况下,新按钮会渐入到新的位置,而不会干扰其他视图;移除时按钮会出现渐出动画,而周围的视图则会平滑地填充移除时的空隙。
我们可以为每个过程自定义过渡动画来实现自定义的动画效果。参见以下代码会向之前的Activity中添加一些自定义过渡动画。
使用了自定义LayoutTransition的Activity
public class MainActivity extends Activity {
LinearLayout mContainer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 布局改变时的动画
mContainer = (LinearLayout) findViewById(R.id.verticalContainer);
LayoutTransition transition = new LayoutTransition();
mContainer.setLayoutTransition(transition);
// 通过翻转进入的动画代替默认的出现动画
Animator appearAnim = ObjectAnimator.ofFloat(null, "rotationY", 90f, 0f)
.setDuration(transition.getDuration(LayoutTransition.APPEARING));
transition.setAnimator(LayoutTransition.APPEARING, appearAnim);
//通过翻转消失的动画代替默认的消失动画
Animator disappearAnim = ObjectAnimator.ofFloat(null, "rotationX", 0f, 90f)
.setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnim);
/**
* 通过滑动动画代替默认的布局改变时的动画
* 我们需要立即设置一些动画属性,所以创建了多个
* PropertyValueHolder对象的动画
*这个动画会让视图滑动进入并短暂地缩小一半长度
*/
PropertyValuesHolder pvhSlide = PropertyValuesHolder.ofFloat("y", 0, 1);
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.5f, 1f);
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.5f, 1f);
Animator changingAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(
this, pvhSlide, pvhScaleY, pvhScaleX)
.setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changingAppearingAnim);
}
public void onAddClick(View v) {
Button button = new Button(this);
button.setText("Click To Remove");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mContainer.removeView(v);
}
});
mContainer.addView(button, new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
}
本例中,我们在Button布局中修改了APPEARING、DISAPPEARING和CHANGE_DISAPPEARING时的过渡动画。前两个过渡动画会影响到视图添加或移除时的效果。当单击Add Item按钮后,新增的按钮会水平旋转进入现有视图中。当单击Remove按钮时,该按钮会在视图中垂直旋转地消失。这两个动画都是通过创建新的ObjectAnimation对象并设置自定义旋转属性来实现的,而动画持续的时间则是每种过渡类型的默认时间(通过一个特定过渡类型的键值设置到我们的LayoutTransition实例上)。最后一种过渡动画稍微有点复杂,需要创建一个动画,让周围的视图可以平滑地运动到新位置,滑动的同时会产生缩放效果。
注意:
在自定义视图改变的过渡动画时,添加移动到视图新位置的动画非常重要,否则在创建视图或填充视图消失区域时可能会出现闪烁的现象。
为了实现这种效果,需要通过PropertyValuesHolder实例创建一个ObjectAnimator来设置一些属性。动画的每个属性都是单独的PropertyValuesHolder,并且通过ofPropertyValuesHolder()工厂方法添加到animator对象中。最后这个过渡动画使Remove按钮下面的所有按钮向上滑动到刚刚空出的位置,同时稍微收缩一下。
Demo下载地址:
1.5 布局变化时的动画