用 Transition 完成 Fragment 共享元素的切换

Material Design 的基础之一是:让不同页面的切换具有意义,即让用户知道与界面发生交互后为何会从当前页面跳转到另一个页面。为此,Android Lolipop 为开发者提供了一个名为 Transition 的过渡动画框架,开发者能够通过这个框架为 Activity 和 Fragment 的跳转提供动画效果。我在了解 Transition 框架的过程中发现现在网上并没有太多将 Transition 应用到 Fragment 上的相关资料,所以我决定写一篇博客来分享一些我学到的知识~

我在开发的一个产品非常简单,用格子的形状来展示图片瀑布流,当用户点击图片时,就会显示该图片的详细信息。多亏了 Transition 框架,我们才能按照我们的想法完成整个图片的交互和显示,效果图如下:

如果你不想看下面的文字,而是想直接下载源码的话,戳我去 Github 下载吧~

在老版本的 Android 系统中会怎样呢?

事实上,这篇博文既为大家带来了好消息,也为大家带来了坏消息。坏消息是:Lolipop 之前的版本并没有可调用的 Transition API;但好消息是:Android support library 提供了可以在 API 21 以上的手机版本中使用 Transition API 的方法,而且不需要为代码添加 API 版本的判断。

Setup

不像为 Activity 添加 Transition,在为 Fragment 添加 Transition 的时候不需要用到 Window.FEATURE_ACTIVITY_TRANSITIONS,也不需要 Window.FEATURE_CONTENT_TRANSITIONS。事实上,你甚至不需要做任何额外的操作就可以完成这项操作了。

Transition Names

Transition 框架需要一个能将当前页面的 View 关联到将要跳转到的页面的办法,而 Lolipop 为我们提供的办法就是:通过名为 “Transition Name” 的 View 属性将两者关联在一起。

下面是为 View 添加 Transition Name 的两种方法:

  • 可以在代码中直接调用 ViewCompat.setTransitionName(),也可以在 Android Lolipop 系统版本以上的设备中直接调用 setTransitionName()。

  • 在布局的 XML 文件中,直接设置 android:transitionName 属性。

在这里有一点是需要注意的:在一个给定的布局中,Transition Name 必须是唯一的,这一点你一定要牢记在心,特别是在使用 ListView 或 RecyclerView 的时候,要不然每一个 Item 都会有相同的 Transition Name。

Set up the FragmentTransaction

为 Fragment 添加 Transition 就像下面的代码看起来那样简单:

getSupportFragmentManager()
        .beginTransaction()
        .addSharedElement(sharedElement, transitionName)
        .replace(R.id.container, newFragment)
        .addToBackStack(null)
        .commit();

只需要调用 addSharedElement() 来关联要与 Fragment 共享的 View。

作为参数传递给 addSharedElement() 的 View 就是第一个 Fragment 中你所想要与即将跳转的 Fragment 共享的 View。而这个方法的第二个参数要求填入的 Transition Name,就是在跳转的 Fragment 中的该共享 View 的 Transition Name。例如,当前 Fragment 中共享 View 的 Transition Name 为 "foo",即将跳转的 Fragment 中的共享 View 的 Transition Name 为 "bar",那么在 addSharedElement() 中传递的第二个参数就应该是 "bar"。

指定 Transition 动画

最后我们需要指定 Fragment 切换时我们需要的 Transition 过渡动画。

为共享元素添加的办法:

  • 调用 setSharedElementEnterTransition() 指定 View 如何从第一个 Fragment 转换为跳转 Fragment 中的 View。

  • 调用 setSharedElementReturnTransition() 指定 View 在用户点击返回按钮后如何从跳转 Fragment 中回到第一个 Fragment 中。

记住,返回 Transition 需要在跳转 Fragment 中调用相应的方法,要不然你得不到你想要的效果的。

当然了,你也可以为任何非共享的 View 设置 Transition 过渡动画,只不过调用的 API 变了:在对应的 Fragment 中调用 setEnterTransition(), setExitTransition(), setReturnTransition(), 和 setReenterTransition() 这些方法就可以了。

这四个方法都只接收一个参数,也就是你所希望使用的 Transition 过渡动画。而我们希望整个切换过程动画不要太复杂,简简单单的就好,所以我们会使用一个自定义 Transition 来完成淡入淡出的弹出效果。

Transition animation classes

Android 已经提供了一些适用于大多数场景的 Transition 动画了,淡入/淡出就是普通的淡入/淡出效果。滑动会让 View 从一端滑动到另一端。爆炸效果看起来蛮有趣的 - View 会从一个点散开。AutoTransition 则会淡入/淡出,移动,大小变化。这些都是简单的 Transition 用例,有兴趣的话可以到 Transition 包中发现更大的世界哦~

public class DetailsTransition extends TransitionSet {
    public DetailsTransition() {
        setOrdering(ORDERING_TOGETHER);
        addTransition(new ChangeBounds()).
                addTransition(new ChangeTransform()).
                addTransition(new ChangeImageTransform()));
    }
}

这里用到的 Transition 就是三个不同的 Transition 过渡动画同时播放的效果:

  • ChangeBounds 显示 View 大小和位置变化的动画

  • ChangeTransform 显示 View 及其父布局的缩放动画

  • ChangeImageTransform 能修改图片的大小或者缩放

如果你想知道这三个 Transition 是怎么交互到一起的,运行我的示例 App,然后把其中的某一个移除就会懂了。

你当然也可以使用 XML 定义更多复杂的 Transition 过渡动画效果,如果你更喜欢用 XML 来定义过渡动画效果的话,不妨看看文件

把所有上面提到的合并到一起

显示示例图的过渡动画的最终代码很简单:

DetailsFragment details = DetailsFragment.newInstance();

// Note that we need the API version check here because the actual transition classes (e.g. Fade)
// are not in the support library and are only available in API 21+. The methods we are calling on the Fragment
// ARE available in the support library (though they don't do anything on API < 21)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    details.setSharedElementEnterTransition(new DetailsTransition());
    details.setEnterTransition(new Fade());
    setExitTransition(new Fade());
    details.setSharedElementReturnTransition(new DetailsTransition());
}

getActivity().getSupportFragmentManager()
        .beginTransaction()
        .addSharedElement(holder.image, "sharedImage")
        .replace(R.id.container, details)
        .addToBackStack(null)
        .commit();

我们用自定义的 DetailsTransition 过渡动画来显示共享元素的跳转和返回动画,其他的 View 都会以淡入/淡出的形式随着动画一起切换。

Android View 体系竟然还能这么理解?

很多小伙伴可能在学习view的绘制流程源码的时候有点抓不住重点,所以在分析代码的时候绕来绕去脑袋晕乎乎的。今天我就来给大家化繁为简,只关注它最核心的东西。

发布于:3天以前  |  37次阅读  |  详细内容 »

移动端常见崩溃指标

崩溃分析,是将 Android 和 iOS 平台常见的 APP 崩溃问题进行归类分析,帮助企业根据崩溃指标快速发现、定位问题。

发布于:5天以前  |  61次阅读  |  详细内容 »

Android死锁初探

说到死锁,大家可能都不陌生,每次遇到死锁,总会让计算机产生比较严重的后果,比如资源耗尽,界面无响应等。

发布于:9天以前  |  108次阅读  |  详细内容 »

Android AES加密(Kotlin)

halo~最近工作上写的东西比较简单,感觉分享不出来,最近刚好看到数据加密这一块,感觉挺不错的,也挺好用的,所以下面分享给大家!

发布于:15天以前  |  93次阅读  |  详细内容 »

Android 持续滑动布局 ConsecutiveScrollerLayout 的使用

在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是WebView,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的ScrollView下嵌套多个RecyclerView、WebView、ScrollView来实现。但是这种嵌套的方式不仅会严重影响布局的性能,而且处理滑动事件的冲突也是一件头疼的事,处理不好会严重影响用户操作的体验。

发布于:18天以前  |  114次阅读  |  详细内容 »

Android 升级适配爬坑历程

最近接手了一个公司项目,项目比较老了,从Android 5.0之后就再也没有适配过了,然而重写时间又来不及,然后我的爬坑之旅便开始了。(以下适配方案是按照项目需求顺序来的)

发布于:21天以前  |  197次阅读  |  详细内容 »

2020年,一文点破跨平台开发框架现状

多年来,跨平台移动开发已经获得了最流行软件开发趋势之一的声誉。这并不令人意外,因为采用跨平台开发技术使得软件工程师使用同一代码就能为不同平台构建应用程序,从而节省时间、金钱以及不必要的工作。

发布于:1月以前  |  217次阅读  |  详细内容 »

最多阅读

简化Android的UI开发 7月以前  |  195147次阅读
Android设计与开发工作流 7月以前  |  2311次阅读
Google Enjarify:可代替dex2jar的dex反编译 1年以前  |  2306次阅读
Android多渠道打包工具:apptools 1年以前  |  1931次阅读
Android权限 - 第一篇 1年以前  |  1903次阅读
Google Java编程风格规范(中文版) 1年以前  |  1884次阅读
Stetho 1年以前  |  1819次阅读
Android UI基本技术点 1年以前  |  1810次阅读
30分钟搭建一个android的私有Maven仓库 1年以前  |  1776次阅读
2015 Google IO带来的新 Android 开发工具 1年以前  |  1712次阅读
你应该知道的布局和属性 1年以前  |  1662次阅读
听FackBook工程师讲*Custom ViewGroups* 1年以前  |  1657次阅读
Gradle小知识#3:任务的顺序 1年以前  |  1637次阅读
MVP在Android平台上的应用 1年以前  |  1636次阅读