android 动画系列 (6) - 转场动画

这是我这个系列的目录,有兴趣的可以看下: android 动画系列 - 目录

专场动画大家熟悉吧,效果绝对炫酷,也是产品汪们动脑筋考研我们的地方,第一效果要炫酷,第二不要卡,简单的专场动画没什么,但是产品要是给你一个复杂的专场动画,那就考验技术功底了,效果要好,速度又要快,真的是有点难度的,所以今天我们来看看这个专场动画,不要有侥幸心理啊,这部分是逃不过去的,必回必备必精的部分。

废话不多说,开始啦,先来理论

何为专场动画

转场动画就是在页面切换时,前一个和后一个页面之间的动画过度,说起来很简单,相信打击也很熟悉,那么我们来说下大家不熟悉的地方。

专场动画的4个状态:

  • enter :进入动画
  • exit : 退出动画
  • reenter : 再次进入动画
  • return : 页面关闭动画

举例:
从 A -> B 页面,一个很熟悉的场景吧:

  • 对于 A 来说是 exit 动画,因为这时是 A 启动 B ,A 就要切换到后台去,所以是退出动画,注意不是关闭动画
  • 对于 B 来说,此时 B 是一个新创建的页面,所以 B 是 enter 进入动画

从 B -> A 页面,点击返回键,回到上个页面

  • 对于 A 来说是 reenter 再次进入动画,因为 A 是从后台切换到前台。
  • 对于 B 来说是 return 关闭动画,因为这时候 B 页面是要销毁的,所以是关闭动画

清楚了上面转场动画的4个状态,下面再去看就清楚多了。


4.X 时代的传统写法

4.x 时代的传统写法,大家一定太熟悉了把 ,操作的是 view 动画

 public void overridePendingTransition(int enterAnim, int exitAnim)

现在再去看这里面的 enter 和 exit 应该就明白是啥意思了吧,之前这里的确是有很多人不清楚的,包括我也是的。

注意点:

  • overridePendingTransition方法必须在startActivity()或者finish()方法的后面
  • 如果参数是0,表示没有动画,尽量不要传0,enter 传0,会是一个黑屏的效果,亲测
startActivity(intent);
overridePendingTransition(R.anim.bottom_top_anim, R.anim.alpha_hide);

---

finish();
overridePendingTransition(R.anim.alpha_show, R.anim.top_bottom_anim);

另外使用主题也是可以的,注意操作的同样是 view 动画

 <style name="ActivityTransitionAnimatoin" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenExitAnimation">@anim/translate_buttom_out</item>
        <item name="android:activityCloseEnterAnimation">@anim/translate_buttom_in</item>
        <item name="android:activityCloseExitAnimation">@anim/translate_top_out</item>
    </style>

    用这个属性去设置
    <item name="android:windowAnimationStyle">@style/ActivityTransitionAnimatoin</item>

这里买的 open 表示 A -> B , close 表示 B -> A

4.x 时代的专场动画没啥好说的,是最简单好用的,产品没什么特殊设计用这个方式就好了。缺点嘛,就是不够灵活,大家发现没,动画添加到谁身上了,是添加到 activity 身上的,所以我们只能给整个页面添加一个统一的动画,而不能针对页面中的控件进行操作。

最简单的我们可以直接使用 android 系统以及给你定义好的动画资源

overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);

windowAnimation和ActivityAnimation的区别

上面我们使用 activityOpenAnimation / activityCloseAnimation 这是在 activity 身上做动画,我们其实还可以在 window 身上做动画,并且window 的级别高于 activity ,两者同时存在,以 window 动画为准。window 的动画我们可以在 theme 上直接加:

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowAnimationStyle">@style/ActivityTransitionAnimatoin</item>
        <item name="android:windowEnterAnimation">@anim/translate_buttom_in</item>
        <item name="android:windowExitAnimation">@anim/translate_buttom_in</item>
    </style>

比 activityOpenAnimation / activityCloseAnimation 动画写起来还方便,不用再键一个 style 出来,不过注意啊,只能设置 下一个页面的动画,因为这里我们只能设置 enter / exit 的动画,具体使用哪种看大家具体需求把。


MD 时代

5.X 的时代我们迎来了革命性的 MD ,google 在 apple 的压力下重新优化了 android 系统上的交互体验,最大的改变是带给了 android 很炫酷的交互体验,这些体验是多方位的,知识点也是很多,这里咱们就来看看新的页面专场动画

不就知道大家听过 transition 变换动画没有,这是 API 19时添加的一种动画,叫过度动画,MD 带给我们的心得炫酷的专场动画就是用 transition 变换动画实现的,transition 本身是比较复杂的,本篇我们不说,下篇再介绍。这里简单说下,transition 是对一个 viewgroup 中所有的 view 做动画,transition 是可以设置这个 viewgroup 是哪个的,我们用 transition 作页面转场动画,就是把 transition 中的视图设置为 页面的根视图。

MD 提供了几个 transition 变换动画的默认实现:

  • Explode :爆炸效果
  • Slide : 侧滑效果
  • fade : 淡入效果
  • Share Element : 共享元素效果

这几个是 MD 默认提供的专场动画效果,是 transition 的子类或间接子类,本质是生成 animator 动画。

那么我们怎么使用呢,我们需要结合 window 来使用了,API 19 添加了 transition 动画后,我们可以给 window 设置进入和退出4种状态对用的动画:

  • setExitTransition() :A中的View退出场景的transition
  • setEnterTransition() :使B中的View进入场景的transition
  • setReturnTransition() - 当B 返回 A时,使B中的View退出场景的transition
  • setReenterTransition() - 当B 返回 A时,使A中的View进入场景的transition

也可以在theme中定义如下style:

  • android:windowExitTransition
  • android:windowEnterTransition
  • android:windowReturnTransition
  • android:windowReenterTransition


    1159344-1f795b3129360658.jpeg

Explode

我们需要 new 一个 transition 动画对象,就是这个 Explode 类,然后设置进 window 就好了

启动页面

 Intent intent = new Intent(this, ExplodeTransitionActivity.class);
        Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle();
        startActivity(intent, options);

在后一个页面设置切换动画

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Explode explode = new Explode();
        getWindow().setEnterTransition(explode);
        getWindow().setExitTransition(explode);

        setContentView(R.layout.activity_mdtarge);
    }
ezgif.com-video-to-gif.gif

可以看到,Explode 根据视图层次自动决定 view 动画的方向是向上还是向下, Explode 类有几个可以设置的属性:


Snip20171225_8.png

看上图,可以设置时间,插值器,延迟时间,其他的都是涉及到 transition 比较很细你的知识了,需要去详细学习 transition 才行。

Slide

使用方法和 Explode 一样

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Slide slide = new Slide();

        getWindow().setEnterTransition(slide);
        getWindow().setExitTransition(slide);
        setContentView(R.layout.activity_mdtarge);
ezgif.com-video-to-gif.gif

Slide 这个动画效果可玩的比较多啊,我们来看看都可以怎么玩


Snip20171225_10.png

除了时间,插值器,延迟时间外,我们还可以设置滑动方向

  Slide slide = new Slide(Gravity.LEFT);
        slide.setDuration(500);

Fade

和 Explode ,Slide 是使用上都是一样的,没什么特殊的设置选项

 Fade fade = new Fade();
        getWindow().setEnterTransition(fade);
        getWindow().setExitTransition(fade);
ezgif.com-video-to-gif.gif

Explode ,Slide,Fade 共同点

为啥我要特意说一下呢,因为这3个 MD 内置效果可以看到用法都一样,都是基于 transition 技术实现页面专场动画的最简单使用。

这里有一个点还要说一下,transition 的转场动画是对页面根节点视图中所有 view 都执行动画,我们可以设置忽略的 view 的 id,那么这个 view 就不会执行转场动画了

忽略底部导航栏 view
slide.excludeTarget(android.R.id.navigationBarBackground, true);
忽略底部状态栏 view
slide.excludeTarget(android.R.id.statusBarBackground, true);

也可以直接在style中设置动画

<item name="android:windowExitTransition">@transition/slide_anim</item>
<item name="android:windowEnterTransition">@transition/slide_anim</item>

其中@transition/slide_anim如下

<!-- @transition/slide_anim-->

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <!--顶部的状态栏以及底部的导航栏不执行动画-->
    <targets>
        <target android:excludeId="@android:id/statusBarBackground"/>
        <target android:excludeId="@android:id/navigationBarBackground"/>
    </targets>

    <slide android:slideEdge="bottom"
        android:duration="1300"/>

    <!--<fade />-->
</transitionSet>

Share Element

共享元素动画是 MD 中最吸引人的专场动画效果了,这个 apple 绝对没有啊,这个做好了看着绝对上档次,目前也是有很多 app 都集成进来了,但是想做好真不容易,在实际开发中,遇到的问题也是很多啊,我这里也是很欠缺的,比如点击列表的一个 item 跳到一个页,跳到的新的页面里面还是列表,然后在这个心得列表里面点击另一个 item再切回来,怎么正确的关联前后页面的共享元素是个大问题啊。

说简单点就是页面切换的动画是从前一个页面的某些元素开始,渐变到整个第二个页面的,废话不多说,看看效果就知道了


ezgif.com-video-to-gif.gif

上面的例子里我们关联了前一个页面和后一个页面中的那个机器人图标的 imageview。说来使用起来也是很简单的:

  • 在 xml 中设置相同的 transitionName 标记用于执行共享元素动画的 view
android:transitionName="imageView"
  • 启动activity B,在启动页面的 bundle 参数中传入共享元素 view 和 transitionName 标记
        Intent intent = new Intent(this, ShadeTransitionActivity.class);
        Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this, view_image, view_image.getTransitionName()).toBundle();
        startActivity(intent, bundle);
  • 在B中设置共享元素动画,这里必要时不设置的话也没事用的是默认的动画样式
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TransitionSet set = new TransitionSet();
        set.addTransition(new ChangeImageTransform());
        set.addTransition(new ChangeBounds());
        getWindow().setSharedElementEnterTransition(set);
        setContentView(R.layout.activity_mdtarge);
    }

简单学下原理:创建出 B 页面对象和 window 后,B 页面解析标签,解析初共享元素,然后和 B 页面的共享元素关联,然后计算出动画的初始参数和结束参数,然后生成动画对象,然后 A 页面隐藏,关闭,B 页面执行这个动画,其中的技术实现是使用 transition 来实现的。

多个共享元素:

 Intent intent = new Intent(this, ShadeTransitionActivity.class);
        Pair<View, String> pair1 = new Pair<View, String>(view_image, ViewCompat.getTransitionName(view_image));
        Pair<View, String> pair2 = new Pair<View, String>(tx_text, ViewCompat.getTransitionName(tx_text));
        Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(this, pair1, pair2).toBundle();
        startActivity(intent, bundle);

这里使用 ActivityOptionsCompat 创建的这个 bundle 对象,推荐还是用这个兼容类好。


Fragment 的页面切换动画

4513808-97ee8c40279a79eb.jpg

enter,exit,reenter,return 这4个位置的动画大家看过上面应该知道都是对应哪个位置的了把,fragment 的替换我们一般来说我们有2种常用的方式:

  • replace 替换
    我们使用上面的图中的方式,加入返回栈就可以实现页面切换的动画
  • show,hint
    这种方式,我们可以使用添加 transition 的方式实现 页面切换动画
 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Slide slide = new Slide(Gravity.LEFT);
        Explode explode = new Explode();

        setEnterTransition(slide);
        setExitTransition(slide);
        setReenterTransition( explode );
        setReturnTransition( explode );
    }

在 fragment 的 onCreate 中添加 transition 动画,我试了下,show,hint 触发的都是 enter,exit 动画,reenter,return 没有触发,这里就麻烦了,要知道 enter,exit,reenter,return 这4个状态是个整个效果,涉及到前后2个页面,这里 reenter,return 触发不出来怎么办,只能让前后2个页面的 enter,exit 执行相反方向的动画了,比如 slide 这个效果,A 页面 left,B 页面 right。

更多的我还得再去找找资料,不过基本的操作就是这样了,fragment 的页面切换我们单纯用的不多,多数使用场景还是在 viewpager 中,vp 的页面切换原理就不是本文写的方式了,是实现 ViewPager.PageTransformer 的这个接口,具体的看这里:ViewPager学习(1) - transformer 页面切换


最后啦

最后了简单的吐槽下吧,这个代码啊,不看不会,不写不明白啊。之前零零散散的关于专场动画这块也是看了还几次了,时间也是花了一些的,时间久一点忘了,就真是一点都不记得了,必须要写 demo,必须要写博客才行,这次看了不少,总算是看全了,以后忘了,10分钟就想起来了。

贴一下本项目的 demo 地址:ActivityTransitionDemo

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