Android动画总结——布局动画、转场动画
之前一篇文章总结了View动画、属性动画、帧动画,这篇文章继续总结布局动画、转场动画。
一、布局动画
布局动画的作用于ViewGroup
,执行动画效果的是内部的子View。布局动画在android中可以通过LayoutAnimation
或LayoutTransition
来实现。
1.LayoutAnimation
LayoutAnimation
实际上是一个View动画,用来控制子View显示时的动画效果。可以通过Java代码或者Xml文件来定义LayoutAnimation
动画。
(1)通过Java代码来定义LayoutAnimation
定义子View的显示动画layout_item_anim_set.xml
:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<translate
android:fromXDelta="500"
android:toXDelta="0"/>
</set>
以ListView
为例,给ListView
设置item的显示动画:
private void setLayoutAnimation() {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.layout_item_anim_set);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mListView.setLayoutAnimation(controller);
}
(2)通过Xml代码来定义LayoutAnimation:
layout_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/layout_item_anim_set"
android:animationOrder="normal"
android:delay="0.5">
</layoutAnimation>
在ListView
所在布局中调用:
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#f1f1f1"
android:dividerHeight="1dp"
android:layoutAnimation="@anim/layout_anim"
android:listSelector="?android:attr/selectableItemBackground"/>
setDelay()
方法以及android:delay
属性表示子View动画显示的延迟时间比例。比如动画执行时间是300ms,延迟比例是0.5,那么延迟时间就是150ms,在Listview中,第一个item在延迟150ms开始动画后,第二个在300ms后开始动画,第三个在450ms后开始,以此类推。
setOder()
方法以及android:animationOrder
表示动画执行的顺序类型,共有三种:normal
表示子View按顺序显示,reverse
表示子View按逆序显示,random
表示子View随机先后显示。
2.LayoutTransition
LayoutTransition
用于在ViewGroup
中有子View添加、删除、隐藏、显示时所有子View动画效果。LayoutTransition
有5中动画变化形式
LayoutTransition.APPEARING
:子View添加到容器中时的动画效果LayoutTransition.CHANGE_APPEARING
:子View添加到容器中时,其他子View位置改变的动画效果
LayoutTransition.DISAPPEARING
:子View被移除时的动画效果
LayoutTransition.CHANGE_DISAPPEARING
:子View被移除时,其他子View的动画效果
LayoutTransition.CHANGING
:子View在容器中位置变化时其他子View的动画效果
(1)使用默认的动画样式
只需要在使用的LinearLayout、FrameLayout、RelativeLayout等ViewGroup容器的布局文件中添加android:animateLayoutChanges="true"
即可,系统会使用默认的LayoutTransition来实现子View添加、删除或变化是的动画效果。
(2)使用自定义动画样式
private void init() {
mContainer = (LinearLayout) findViewById(R.id.container);
setLayoutTransition();
}
private void setLayoutTransition() {
LayoutTransition transition = new LayoutTransition();
// 子View添加到mContainer时的动画
Animator appearAnim = ObjectAnimator
.ofFloat(null, "rotationX", 90, 0)
.setDuration(transition.getDuration(LayoutTransition.APPEARING));
transition.setAnimator(LayoutTransition.APPEARING, appearAnim);
// 子Veiw从mContainer中移除时的动画
Animator disappearAnim = ObjectAnimator
.ofFloat(null, "rotationX", 0, 90)
.setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnim);
// 子Veiw添加到mContainer中时其他子View的动画
PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
PropertyValuesHolder pvhTranslationY = PropertyValuesHolder
.ofFloat("translationX", 0, 150, 0);
Animator changeAppearAnim = ObjectAnimator
.ofPropertyValuesHolder(mContainer, pvhLeft, pvhTop, pvhTranslationY)
.setDuration(transition.getDuration(LayoutTransition.CHANGE_APPEARING));
transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAppearAnim);
// 子View从mContainer中移除时其他子View的动画
PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left", 0, 0);
PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top", 0, 0);
PropertyValuesHolder pvhTranslationYDis = PropertyValuesHolder
.ofFloat("translationX", 0, -150, 0);
ObjectAnimator changeDisAppearAnim = ObjectAnimator
.ofPropertyValuesHolder(mContainer, outLeft, outTop, pvhTranslationYDis)
.setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisAppearAnim);
mContainer.setLayoutTransition(transition);
}
// 添加子View到第0个位置
public void addData(View view) {
View child = LayoutInflater.from(this)
.inflate(R.layout.item, mContainer, false);
mContainer.addView(child, 0);
}
// 移除第0个子View
public void deleteData(View view) {
if (mContainer.getChildCount() != 0) mContainer.removeViewAt(0);
}
在使用PropertyValuesHolder
时,需要注意一下几点:
1.LayoutTransition.CHANGE_APPEARING
和LayoutTransition.CHANGE_DISAPPEARING
必须使用PropertyValuesHolder
构造动画才会有效果,其他任何方式构造动画都不会有效果。
2.在使用PropertyValuesHolder
时,”left”
、”top”
属性就算不需要变化也必须要写,如果不需要变化可以写成:
PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
3.ofInt
,ofFloat
中的参数值,第一个值和最后一个值必须相同,不然此属性所对应的的动画将被放弃,在此属性值上将不会有效果:
PropertyValuesHolder pvhTranslationY = PropertyValuesHolder
.ofFloat("translationX", 0, 150, 0);
4.使用的ofInt
,ofFloat
中,如果所有参数值都相同,也将不会有动画效果。
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
动画的参数全部相同,left这个属性的动画不会有任何效果。
说明:关于PropertyValuesHolder
几个注意点参考自文章animateLayoutChanges与LayoutTransition。
二、转场动画
1.Android 5.0之前转场动画
在Android 5.0以前实现转场动画是通过补间动画来实现,通常是在Activity
中是overridePendingTransition(int enterAnim, int exitAnim)
方法。
enterAnim
和exitAnim
两个参数对应的是两个View动画:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anim);
setStartActivityAnim();
}
private void setStartActivityAnim() {
overridePendingTransition(R.anim.activity_right_in, R.anim.activity_left_out);
}
入场动画activity_right_in.xml
:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="100%p"
android:toXDelta="0"/>
出场动画activity_left_out.xml
:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="0"
android:toXDelta="-100%p"/>
在Activity
的onCreate
中调用overridePendingTransition
方法只对主动启动Activity
有效,如果我们返回上一个Activity
也需要同样的转场动画,就需要在finish
方法也添加上这个方法。
@Override
public void finish() {
super.finish();
setEndActivityAnim();
}
private void setEndActivityAnim() {
overridePendingTransition(R.anim.activity_left_in, R.anim.activity_right_out);
}
finish
时入场动画activity_left_in.xml
:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="-100%p"
android:toXDelta="0"/>
finish
时出场动画activity_right_out.xml
:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="0"
android:toXDelta="100%p"/>
Android 5.0前转场动画效果
2.Android 5.0之后转场动画 Activity Transition
Android 5.0之后,谷歌引入了 Activity Transition 来实现交互更加友好的转场动画效果。
Tansition的类型共有三种:
进入 —— 决定Activity中的所有视图怎么进入屏幕
退出 —— 决定Activity中的所有视图怎么退出屏幕
共享元素 —— 决定两个Activity之间的过渡时怎么共享它们的视图
进入和退出包含如下动画效果:
explode(分解) —— 从屏幕中间进或出
slide(滑动) —— 从屏幕边缘进或出地
fade(淡出) —— 改变屏幕上视图的不透明度实现添加或移除视图的效果
共享元素包含如下动画效果:
changeBounds —— 改变目标视图的布局边界
changeClipBounds —— 裁剪目标视图边界
changeTransform —— 改变目标视图的缩放比例和旋转角度
changeImageTransform —— 改变目标图片的大小和缩放比例
说明:Tansition分类及动画效果说明参考《Android群英传》
先来看下Explode
(分解)、Slide
(滑动)、Fade
(淡出)三种转场动画的使用。
第一个Activity
中:
public void explode(View view) {
Intent intent = new Intent(this, NextTransitionActivity.class);
intent.putExtra("flag", "explode");
startActivity(intent,
ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
}
public void slide(View view) {
Intent intent = new Intent(this, NextTransitionActivity.class);
intent.putExtra("flag", "slide");
startActivity(intent,
ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
}
public void fade(View view) {
Intent intent = new Intent(this, NextTransitionActivity.class);
intent.putExtra("flag", "fade");
startActivity(intent,
ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
}
启动的第二个Activity
中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
switch (getIntent().getStringExtra("flag")) {
case "explode":
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
break;
case "slide":
getWindow().setEnterTransition(new Slide());
getWindow().setExitTransition(new Slide());
break;
case "fade":
getWindow().setEnterTransition(new Fade());
getWindow().setExitTransition(new Fade());
break;
}
setContentView(R.layout.activity_next_transition);
}
在第二个Activity
中可以通过在style中配置<item name="android:windowContentTransitions">true</item>
就不需要调用getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
。
共享元素转场动画,使用也比较简单:
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TransitionAdapter.TransitionViewHolder holder = (TransitionAdapter.TransitionViewHolder) view.getTag();
View shareViewImg = holder.civImg;
View shareViewName = holder.tvName;
Intent intent = new Intent(this, NextTransitionActivity.class);
intent.putExtra("flag", "share");
startActivity(intent, ActivityOptionsCompat
.makeSceneTransitionAnimation(this,
Pair.create(shareViewImg, "shareView_img"),
Pair.create(shareViewName, "shareView_name"))
.toBundle());
}
这里使用ListView
实现一个列表,列表中的头像和名字作为共享元素。在ListView
的item
的布局以及第二个启动的Activity
的布局中,被共享的View
都需要在布局文件中添加上相同android:transitionName
属性,当然也可以在Java代码中通过ViewCompat.setTransitionName(View view, String transitionName)
方法来设置共享View
的transitionName
。
我们给头像和名称分别指定android:transitionName="shareView_img"
和android:transitionName="shareView_name"
。
点击item
后启动目标Activity
时,指定的options
参数为:
ActivityOptionsCompat.makeSceneTransitionAnimation(this,
Pair.create(shareViewImg,"shareView_img"),
Pair.create(shareViewName, "shareView_name"))
.toBundle());
通过options
参数,可以利用Pair
构造多个共享元素,但是共享元素View
的共享名称transitionName
必须一一对应。