Android动画总结——布局动画、转场动画

之前一篇文章总结了View动画、属性动画、帧动画,这篇文章继续总结布局动画、转场动画。


一、布局动画

布局动画的作用于ViewGroup,执行动画效果的是内部的子View。布局动画在android中可以通过LayoutAnimationLayoutTransition来实现。

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随机先后显示。

LayoutAnimation效果图

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添加、删除或变化是的动画效果。

默认的LayoutTransition效果

(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_APPEARINGLayoutTransition.CHANGE_DISAPPEARING必须使用PropertyValuesHolder构造动画才会有效果,其他任何方式构造动画都不会有效果。
2.在使用PropertyValuesHolder时,”left””top”属性就算不需要变化也必须要写,如果不需要变化可以写成:

PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);

3.ofIntofFloat中的参数值,第一个值和最后一个值必须相同,不然此属性所对应的的动画将被放弃,在此属性值上将不会有效果:

PropertyValuesHolder pvhTranslationY = PropertyValuesHolder
            .ofFloat("translationX", 0, 150, 0);

4.使用的ofIntofFloat中,如果所有参数值都相同,也将不会有动画效果。

PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);

动画的参数全部相同,left这个属性的动画不会有任何效果。

说明:关于PropertyValuesHolder几个注意点参考自文章animateLayoutChanges与LayoutTransition

自定义动画样式配置LayoutTransition效果


二、转场动画

1.Android 5.0之前转场动画

在Android 5.0以前实现转场动画是通过补间动画来实现,通常是在Activity中是overridePendingTransition(int enterAnim, int exitAnim)方法。
enterAnimexitAnim两个参数对应的是两个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"/>

ActivityonCreate中调用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实现一个列表,列表中的头像和名字作为共享元素。在ListViewitem的布局以及第二个启动的Activity的布局中,被共享的View都需要在布局文件中添加上相同android:transitionName属性,当然也可以在Java代码中通过ViewCompat.setTransitionName(View view, String transitionName)方法来设置共享ViewtransitionName
我们给头像和名称分别指定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必须一一对应。

共享元素转场动画效果


源码:https://github.com/xiaoyanger0825/AnimationSummary

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,415评论 22 663
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,612评论 4 59
  • 六一儿童节快乐,希望身边的人都能拥有一颗儿童之心,保持点儿童般的纯真和快乐,或许我们的生活会有不同的变化。 所为人...
    开拓者2021阅读 259评论 0 0
  • 夜色深如海,寒气疾若潮。 恼人秋风逐花凋。 香冷高楼无绪, 诗吟度残宵。 试墨远笺瘦, 凭栏叹梦遥。 鬓丝依旧为谁...
    逸塵居士阅读 184评论 0 0