使用 Material Design 组件实现 Material 动效
容器转换 (Container transform): https://material.io/design/motion/the-motion-system.html#container-transform
共享轴 (Shared axis): https://material.io/design/motion/the-motion-system.html#shared-axis 淡入淡出 (Fade through): https://material.io/design/motion/the-motion-system.html#fade-through 褪色 (Fade): https://material.io/design/motion/the-motion-system.html#fade
Material 动效系统
https://material.io/design/motion/the-motion-system.htmlMDC-Android 库 (v 1.2.0)
https://medium.com/google-design/material-components-for-android-1-2-0-is-now-available-aade483ed841
我们已经在 Android 平台和 AndroidX 过渡系统实现了以上过渡效果,以便在 Activity、Fragment 和 View 之间切换时轻松使用。
Android 平台
https://developer.android.google.cn/reference/android/transition/package-summaryAndroidX 过渡系统
https://developer.android.google.cn/reference/androidx/transition/package-summary
如果您不满足于上手介绍,更希望深入源码,请参阅 Material 动效 Codelab,按步骤上手实践这项技术,Codelab 也提供了在 Android 上使用这些过渡效果的其他信息。
Reply
https://material.io/design/material-studies/reply.htmlMaterial 动效 Codelab
https://codelabs.developers.google.com/codelabs/material-motion-android
容器转换: 打开邮件
FAB
https://material.io/components/buttons-floating-action-buttonchip
https://material.io/components/chips卡片
https://material.io/components/cards导航上下文
https://material.io/design/navigation/navigation-transitions.html#hierarchical-transitions
HomeFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/home/HomeFragment.ktEmailFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/email/EmailFragment.ktAndroid 共享元素过渡
https://developer.android.google.cn/training/transitions/start-activity
android:transitionName="@{@string/email_card_transition_name(email.id)}"
过渡名称
https://developer.android.google.cn/reference/android/view/View#attr_android:transitionName数据绑定
https://developer.android.google.cn/topic/libraries/data-bindingEmailFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/email/EmailFragment.kt
这两个视图会被我们的容器转换使用。工作原理是: 它们都会被放在一个 drawable 内部,此 drawable 的边界会被裁剪到 "容器" 中,而 "容器" 会将自己的形状通过动画从一个列表项转换为详情页。在过渡过程中,通过传入页面在传出屏幕上淡入,容器的内容 (列表项和详情页) 发生了交换。
现在我们已经标记了共享元素的视图,接下来就可以创建目的地 Fragment 的 sharedElementEnterTransition,并将其设置给一个 MaterialContainerTransform 的实例。默认情况下,从详情页面返回时,这个 sharedElementEnterTransition 会自动反转并播放。
sharedElementEnterTransition = MaterialContainerTransform().apply {
// drawingViewId 是视图的 id,在其上方,容器变换将在 z 轴空间进行
drawingViewId = R.id.nav_host_fragment
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
// 由于我们也想将列表页面通过动画转换出视图,所以将 scrimColor 设置为透明
scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().themeColor(R.attr.colorSurface))
}
有关 MaterialContainerTransform 参数的详细信息,请参阅动效文档: https://github.com/material-components/material-components-android/blob/master/docs/theming/Motion.md#container-transform
当一封邮件被点击时,我们所有需要做的就是为 Fragment 事务提供开始视图和结束视图过渡名称之间的映射。有了这些信息,邮箱详情 Fragment 共享元素过渡就可以使用我们提供的 MaterialContinaerTransform 找到并在两个视图之间进行动画切换。
override fun onEmailClicked(cardView: View, email: Email) {
exitTransition = MaterialElevationScale(false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
val emailCardDetailTransitionName = getString(R.string.email_card_detail_transition_name)
val extras = FragmentNavigatorExtras(cardView to emailCardDetailTransitionName)
val directions = HomeFragmentDirections.actionHomeFragmentToEmailFragment(email.id)
findNavController().navigate(directions, extras)
}
在上面的代码片段中,我们也为传出页邮件列表 Fragment 设置了 exit 和 reenter 的过渡效果。Material 组件提供了两个过渡辅助: Hold 和 MaterialElevationScale,以平滑地为将要被替换的 Fragment 设置动画。除了褪色 (Fade),MaterialElevationScale 还会在邮件列表页退出时,对其进行缩放,并在重新进入邮件列表时缩放回来。Hold 仅仅是简单地保留邮件列表。如果没有设置退出时的过渡,我们的邮件列表会被立刻删除并从视图中消失。
Hold
https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/Hold.javaMaterialElevationScale
https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/MaterialElevationScale.java
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
在您自己的应用中,您可能需要尝试这两种方法,以根据您填充 UI 的方式和时间,来找到合适的时间开始延迟过渡。如果您发现您的返回动画没有执行,可能是在共享元素就绪之前开始了过渡。
接下来进入我们的搜索页面。
共享轴: 打开搜索页面
共享轴模式用于有空间和导航关系的 UI 元素之间的过渡。在 Reply 应用中,打开搜索页面会将用户带到邮件列表顶部的新页面。为了介绍这个三维模型,我们可以在邮件列表 (HomeFragment) 和搜索页面 (SearchFragment) 之间使用共享 z 轴过渡。
HomeFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/home/HomeFragment.ktSearchFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/search/SearchFragment.kt
共享轴过渡会在操作两个目标的同时创建最终的、编排过的过渡效果。这意味着 "成对" 的过渡会一起运行去创建连续的定向的动画。对 Fragment 来说,这成对的过渡包括:
FragmentA 的 exitTransition 和 FragmentB 的 enterTransition FragmentA 的 reenterTransition 和 FragmentB 的 returnTransition
如需了解更多关于共享轴方向性的详细信息,请查阅动效文档: https://material.io/design/motion/the-motion-system.html
MaterialSharedAxis
https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/MaterialSharedAxis.java
在 Reply 应用中,这是我们为当前的 Fragment (HomeFragment) 建立退出和重入过渡的方法。
currentNavigationFragment?.apply {
exitTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ true
).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ false
).apply {
duration =resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
HomeFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/home/HomeFragment.kt
在我们目的 fragment (SearchFragment) 中,我们建立进入和返回的过渡。
enterTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ true
).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
returnTransition = MaterialSharedAxis(
MaterialSharedAxis.Z,
/* forward= */ false
).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
SearchFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/search/SearchFragment.kt
注意: 当前 Fragment 的退出过渡和搜索 Fragment 的进入过渡使用相同的 forward 值 - true,当前 Fragment 的重入过渡和搜索 Fragment 的返回过渡也是如此。
接下来,默认情况下,过渡会在场景根层次结构内的所有子视图上运行,这意味着一个共享轴过渡会应用于邮件列表上的每一封邮件以及搜索页面的每一个子视图。如果您想要 "传播" 或者 "错开" 动画,这是一个非常好的功能,但是由于我们需要对每个 Fragment 的根作为整体进行动画处理,我们需要在邮件列表的 RecyclerView 和我们的搜索页面的根 view group 设置 android:transitionGroup="true"。
错开
https://material.io/archive/guidelines/motion/choreography.html#choreography-creation邮件列表的 RecyclerView
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/res/layout/fragment_home.xml#L18搜索页面的根 view group
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/res/layout/fragment_search.xml#L17
这样,我们就在进出搜索页面时有了一个漂亮的共享 z 轴过渡!共享轴是一个非常灵活的过渡,可以应用于许多不同的场景,从页面过渡到智能回复选择,再到进入或者垂直的步骤流程。您已经配置好了设置,还可以尝试使用 MaterialSharedAxis 的 axis 参数来了解其他轴动画是什么样子。
MaterialSharedAxis
https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/MaterialSharedAxis.java
淡入淡出: 切换邮箱
HomeFragment
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/java/com/materialstudies/reply/ui/home/HomeFragment.kt
由于 MaterialFadeThrough 没有方向性,所以设置起来更加简单。我们只需要为传出 Fragment 设置一个退出过渡,为传入 Fragment 设置一个进入过渡。
currentNavigationFragment?.apply {
exitTransition = MaterialFadeThrough().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
enterTransition = MaterialFadeThrough().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
MaterialFadeThrough
https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/MaterialFadeThrough.java
邮件列表的 RecyclerView
https://github.com/material-components/material-components-android-examples/blob/develop/Reply/app/src/main/res/layout/fragment_home.xml#L25
以上就是淡入淡出过渡!您可以在自己项目有趣的地方来使用淡入淡出模式,比如: 底部导航栏的切换、列表项的交换,或替换一个工具栏菜单。
一往无前!
动效规范
https://material.io/design/motion/the-motion-system.html
Material 动效开发文档: 您可以在 Material Android 动效文档找到许多关于在 Activity 和 View 之间进行动画的自定义选项和建议。
https://github.com/material-components/material-components-android/blob/master/docs/theming/Motion.md
Material 动效 Codelab: 一个完整的分步的开发者教程,内容涉及如何在 Reply 应用中添加 Material 动效。
https://codelabs.developers.google.com/codelabs/material-motion-android/#0
Android Google 云盘: 您可以在 Android Google 云盘应用中看到正在运行的动效系统。点击文件夹、打开搜索、在底部导航间切换,这些都用到了 MDC-Android 的过渡效果。
https://play.google.com/store/apps/details?id=com.google.android.apps.docs
👆点击获取 "开发者的日常" 表情包
推荐阅读