Jetpack Compose lowers the threshold of animation realization-from "slowly polish if you have time" to "animation realization is very simple, there is no reason not to try it." There is a big topic here is the page-level transition animation, which is also Navigation Compose has been trying to solve, specifically to meet the following three scenarios:
- Only use the stable animation API in Compose 1.0.0
- Started to provide support for the experimental animation API existing in Compose 1.0.0
- Future-oriented animation API (shared element transition) built in Compose 1.1.0 and later
The implementation method of each situation is slightly different, we will introduce in this article.
Compose 💚 Animation
It has been a long process from the first release of Jetpack Compose 0.1.0-dev01 to the latest Compose 1.0.1. Compared with the View system, one of its huge improvements is animation and transition. In the process of pursuing the perfect animation API, a lot of changes were made to Compose to iterate step by step to version 1.0.0 .
Although many low-level animation APIs, such as the very powerful animateTo()
and animate*AsState()
are the stable basic components of Compose so far, there are still many APIs built on these codes that are marked as @ExperimentalAnimationApi
.
Experimental API and semantic version control
Experimental API (any API annotated with @RequiresOptIn in the Kotlin field) may be changed at any time. This means that these APIs may be changed, optimized or replaced in any future version (perhaps Compose 1.1.0-alpha04 or 1.2.0-alpha08). Therefore, if you use any library built based on these experimental APIs, when you update the version of Compose you are using but do not update the versions of these libraries at the same time, these libraries may directly crash and fail to build. (If you have used an earlier version of Compose, you will know the pain.)
All AndroidX libraries (including Navigation and Compose), follow strict semantic versioning , such as AndroidX version of the page said. This means that once a library is iterated to a candidate version (Release Candidate, or RC), any non-experimental API will not be changed. Breaking changes to these stable APIs requires increasing the major version number (eg, '2.0').
This is very friendly to forward and backward compatibility. For example, you can upgrade the Fragment version to try new alpha content, while keeping other dependencies on its stable version, and everything works as usual.
However, this also means that experimental APIs (that is, APIs that can be removed from your bottom layer) are strictly prohibited from being used across different libraries. For example, upgrading your androidx.fragment version should not break androidx.appcompat. This rule also applies to androidx.navigation and androidx.compose.animation.
makes Navigation 2.4 stable
Navigation 2.4 is an important version. It is not only the first Navigation Compose version, but also the first to support multi-return stack for Navigation Compose and Navigation with Fragments. This means that we are sorting out the remaining related API requirements to prepare to pass the beta, RC and stable versions.
For Navigation Compose, this means that we are building on Compose 1.0.1 and providing forward compatibility for developers who want (or have already) started relying on Compose 1.1.0-alpha01 or later.
This forward compatibility requirement means that any code in Navigation Compose 2.4.0 can only rely on the stable Compose animation API. This is also the way we Navigation 2.4.0-alpha05 -in the world of Compose, you should first eliminate blunt page jumps.
This limitation of using only stable Compose animation API means that Navigation 2.4 cannot directly use AnimatedContent ), and you cannot use them directly as part of Navigation 2.4 to achieve that rich animation control. However, the extensibility of Navigation means that the underlying framework has been built and is available.
introduction: Accompanist navigation animation!
The support for animation switching between destinations is Accompanist Navigation Animation , which is based on the recently released Navigation 2.4.0-alpha06 . The navigation animation library provides a set of animated versions for the Navigation Compose API you have been using:
- Use
rememberAnimatedNavController()
replacerememberNavController()
- Use
AnimatedNavHost
replaceNavHost
- Use
import com.google.accompanist.navigation.animation.navigation
replaceimport androidx.navigation.compose.navigation
- Use
import com.google.accompanist.navigation.animation.composable
replacement Import androidx.navigation.compose.composable
At first glance, the appearance of your application has not changed-the default animations are still fadeIn and fadeOut types, which are the same as the fade types provided in Navigation 2.4. However, you will get an important new feature- can configure these animations and replace your own transition animation between pages.
Each composable destination has four new parameters that can be set:
enterTransition
: Specify the animation to be executed when you usenavigate()
exitTransition
: Specify the animation to be executed when you leave the destination by navigating to another destination.popEnterTransition
: Specify the animation to be executed when the destination re-enters the arena after calling popBackStack(). The default is enterTransition.popExitTransition
: Specify the animation to be executed when the destination leaves the screen by popping the return stack. The default isexitTransition
.
In each case, these parameters have the same format:
enterTransition: (
(
initial: NavBackStackEntry,
target: NavBackStackEntry
) -> EnterTransition?
)? = null,
Each parameter receives a lambda. The lambda has two NavBackStackEntry
type 0613ecccf58f0e, which respectively indicate where you are from ( initial
) and where you want to go ( target
). Take enterTransition
as an example, the destination that will be entered is target
-that is, the destination enterTransition
The exitTransition
is true for initial
: 0613ecccf58f17 is the destination where the exit animation will be executed.
This allows you to write the destination like this:
composable(
"profile/{id}",
enterTransition = { _, _ ->
// 让我们写一个很长的淡入
fadeIn(animationSpec = tween(2000)
}
) {
// 像往常一样添加内容
}
Or, control your animation based on where you come from/go:
composable(
"friendList"
exitTransition = { _, target ->
when (target.destination.route) {
"profile/{id}" -> ExitTransition.fadeOut(
animationSpec = tween(2000)
) // 慢慢地淡出
else -> null // 使用默认值
}
}
) {
// 像往常一样添加内容
}
composable(
"profile/{id}",
enterTransition = { initial, _ ->
when (initial.destination.route) {
"friendList" -> slideInVertically(
initialOffsetY = { 1800 }
) // 滑入 profile 页面
else -> null // 使用默认值
}
) {
// 像往常一样添加内容
}
Here, the friendList page controls the transition animation from exiting to the profile page, and the profile page controls the transition animation from entering the friendList page, and allows custom sliding animations between these two destinations. At the same time, we can use null
mean "use the default value". These default values come from the parent navigation graph, the parent navigation graph of the parent navigation graph, and up to the root AnimatedNavHost
. This means that you want to set a default animation (for example, cross-fade time), only in your AnimatedNavHost
modify global in enterTransition
and exitTransition
.
If you only want to modify the default value of a submap (for example, the page in your login submap always uses horizontal sliding animation), you can also set the animation at the nested image level:
navigation(
startDestination = "ask_username"
route = "login"
enterTransition = { initial, _ ->
// 检查上一个页面是否在登录子图中
if (initial.destination.hierarchy.any { it.route == "login" }) {
slideInHorizontally(initialOffsetX = { 1000 }
} else
null // 使用默认值
}
exitTransition = { _, target ->
// 检查新的页面是否在登录子图中
if (target.destination.hierarchy.any { it.route == "login" }) {
slideOutHorizontally(targetOffsetX = { -1000 }
} else
null // 使用默认值
}
popEnterTransition = { initial, _ ->
// 检查上一个页面是否在登录子图中
if (initial.destination.hierarchy.any { it.route == "login" }) {
// 请注意我们在 pop 操作时从相反的方向做动画
slideInHorizontally(initialOffsetX = { -1000 }
} else
null // 使用默认值
}
popExitTransition = { _, target ->
// 检查新的页面是否在登录子图中
if (target.destination.hierarchy.any { it.route == "login" }) {
// 请注意我们在 pop 操作时从相反的方向做动画
slideOutHorizontally(targetOffsetX = { 1000 }
} else
null // 使用默认值
}
) {
composable("ask_username") {
// 添加内容
}
composable("ask_password") {
// 添加内容
}
composable("register") {
// 添加内容
}
}
Please note that we use the hierarchy extension method ()) to determine whether a destination is part of the login subgraph-in this way, we enters the login subgraph and leaves the login subgraph transition animation The default value (or any transition animation you set at a higher level) will be used.
Whenever you have a directional transition animation, such as horizontal sliding, the difference between enterTransition and popEnterTransition is very convenient-you will be able to avoid causing one page to slide to the right and the other page to slide to the left.
Accompanist acts as a booster for the Jetpack library, allowing us to immediately get experimental features during the development of Compose 1.1.
Add Accompanist navigation animation dependency:
implementation "com.google.accompanist:accompanist-navigation-animation:0.16.0"
Navigation Compose and the future of animation
With the navigation 2.4 based on Compose 1.0.1 and the Accompanist navigation animation library breaking through the limitations of Compose 1.0 through the experimental API, there will be other content coming soon: Compose 1.1. Through the Compose roadmap can find that there is a very important and exciting feature coming soon:
supports shared element transition
Our goal for Navigation 2.5 is to bring all the advantages of Compose 1.1 to Navigation Compose. This means that when the animation API is removed from the experimental state, we can directly bring it to Navigation Compose. This also means that we can build APIs that support the transition of shared elements.
This also means that Accompanist navigation animation should be regarded as a temporary measure: once Navigation Compose itself provides the same level of animation API (tailored based on your feedback), you can directly rely on it and you can completely remove Accompanist Navigate the animation library.
keep going
Balancing stability and the forward and backward compatibility requirements we put forward to ourselves as a Jetpack library, and the ability to deliver functions quickly, is not as simple as we thought. With the continuous development of Jetpack Compose, Accompanist is a great boon for the ever-advanced needs. I would like to thank Chris Banes and all the developers who invested time on Accompanist, the entire team behind Compose, and everyone who helped shape the future of Android development.
Note : If you are looking for more information about Navigation+Accompanist, please refer to:
Welcome Click here submit feedback to us, or share your favorite content found problems. Your feedback is very important to us, thank you for your support!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。