乐趣区

关于android:动画实现更简单Navigation-Compose-帮您忙

Jetpack Compose 将动画实现的门槛升高了——从 “ 如果有工夫再缓缓打磨 ” 到 “ 动画实现很简略,没有理由不试试看了 ”。这里有个很大的课题是页面级的过渡动画,这也是 Navigation Compose 始终致力解决的问题,具体是满足上面三种场景:

  • 仅应用 Compose 1.0.0 中稳固的动画 API
  • 开始对 Compose 1.0.0 中存在的实验性动画 API 提供反对
  • 构建在 Compose 1.1.0 及更高版本中面向未来的动画 API (共享元素过渡)

每一种状况的实现办法都稍有不同,咱们将在本文中介绍。

Compose 💚 动画

从首次公布 Jetpack Compose 0.1.0-dev01 到最新的 Compose 1.0.1,经验了漫长的过程。绝对于 View 零碎而言,它微小的改良之一便是动画和过渡。在谋求完满的动画 API 的过程中,对 Compose 进行了大量的批改才一步步迭代到 版本 1.0.0。

尽管许多底层的动画 API,比方十分弱小的 animateTo()animate*AsState() 到目前为止是 Compose 稳固的根底形成局部,但仍有许多基于这些代码构建的 API 被标记为 @ExperimentalAnimationApi

实验性 API 和语义化版本控制

实验性 API (任何在 Kotlin 畛域应用 @RequiresOptIn 注解的 API) 可能随时会被更改。这意味着这些 API 可能在将来任一版本 (可能是 Compose 1.1.0-alpha04 或者 1.2.0-alpha08) 中被更改、优化或替换。因而,如果您应用了任何一个基于这些实验性 API 构建的库,当您更新了您应用的 Compose 版本但没有同时更新这些库的版本时,这些库可能会间接解体并构建失败。(如果您应用了晚期公布的 Compose 版本,您就会晓得这种苦楚。)

所有 AndroidX 库 (包含 Navigation 和 Compose),都遵循 严格的语义化版本控制,如 AndroidX 版本页面 所述。这意味着一旦某个库迭代至候选版本 (Release Candidate,即 RC),任何非实验性 API 将不会再被更改。对这些稳固的 API 进行破坏性变更须要减少主版本号 (如,’2.0′)。

这对向前和向后兼容很敌对。例如,您能够降级 Fragment 版本以尝试新的 alpha 内容,同时将其余依赖项放弃在其稳固版本上,所有工作如常。

然而,这也意味着严格禁止实验性 API (即能够从您底层移除的 API) 逾越不同的库应用。例如,降级您的 androidx.fragment 版本不应该毁坏 androidx.appcompat。这一规定同样利用于 androidx.navigation 和 androidx.compose.animation。

使 Navigation 2.4 稳固

Navigation 2.4 是一个重要的版本,它既是第一个 Navigation Compose 版本,也是第一个对 Navigation Compose 和带有 Fragment 的 Navigation 反对 多返回栈 的版本。这意味着咱们正在整顿残余的相干 API 需要以筹备通过测试版、RC 版和稳定版。

对于 Navigation Compose 而言,这意味着咱们正基于 Compose 1.0.1 进行构建,并为那些想要 (或者曾经) 开始依赖 Compose 1.1.0-alpha01 或更新版本的开发者提供向前兼容。

这种向前兼容性要求意味着 Navigation Compose 2.4.0 的任何代码只能依赖于稳固的 Compose 动画 API。这也是咱们在 Navigation 2.4.0-alpha05 中减少穿插淡入淡出反对的形式——在 Compose 的世界中,您应该首先打消僵硬的页面跳转。

这种仅应用稳固 Compose 动画 API 的限度意味着 Navigation 2.4 不能间接应用 AnimatedContent) 之类的 API,您不能将它们间接作为 Navigation 2.4 的一部分来应用以实现那种丰盛的动画管制。然而,Navigation 的可扩展性意味着底层框架曾经被构建好了并且是可用的。

介绍: Accompanist 导航动画!

对于目的地之间动画切换的反对是咱们能公布 Accompanist Navigation Animation 的起因,它基于最近公布的 Navigation 2.4.0-alpha06。导航动画库为您始终在应用的 Navigation Compose API 提供一套带动画的版本:

  • 应用 rememberAnimatedNavController() 替换 rememberNavController()
  • 应用 AnimatedNavHost 替换 NavHost
  • 应用 import com.google.accompanist.navigation.animation.navigation 替换 import androidx.navigation.compose.navigation
  • 应用 import com.google.accompanist.navigation.animation.composable 替换 import androidx.navigation.compose.composable

乍一看,您利用的外观没有产生扭转——默认动画依然是 fadeIn 和 fadeOut 类型,与 Navigation 2.4 中所提供的淡入淡出类型雷同。然而,您将取得一项重要的新性能——可能配置这些动画并在页面之间替换您本人的过渡动画

每个 composable 目的地都有四个新参数能够设置:

  • enterTransition: 指定当您应用 navigate() 导航至该目的地时执行的动画。
  • exitTransition: 指定当您通过导航至另一个目的地的形式来到该目的地时执行的动画。
  • popEnterTransition: 指定当该目的地在通过调用 popBackStack() 后从新入场时执行的动画。默认为 enterTransition。
  • popExitTransition: 指定当该目的地在以弹出返回栈的形式来到屏幕时执行的动画。默认为 exitTransition

在每种状况下,这些参数都具备雷同的格局:

enterTransition: (
   (
       initial: NavBackStackEntry,
       target: NavBackStackEntry
   ) -> EnterTransition?
)? = null,

每个参数都接管一个 lambda。该 lambda 有两个 NavBackStackEntry 类型的参数,别离示意您来自何处 (initial) 和您要去往何处 (target)。以 enterTransition 为例,将要进入的目的地为 target—— 也就是将要启用 enterTransition 的目的地。而 exitTransition 则相同: initial 为将要执行退出动画的目的地。

这使得您能够像这样编写目的地:

composable("profile/{id}",
  enterTransition = { _, _ ->
    // 让咱们写一个很长的淡入
    fadeIn(animationSpec = tween(2000)
  }
) {// 像平常一样增加内容}

或者,依据您来自 / 去往何处来管制您的动画:

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 // 应用默认值
  }
) {// 像平常一样增加内容}

在这里,friendList 页管制其退出到 profile 页的过渡动画,profile 页管制其从 friendList 页进入的过渡动画,并且容许在这两个目的地之间自定义滑动动画。同时,咱们能够应用 null 示意 “ 应用默认值 ”。这些默认值顺次来自父导航图、父导航图的父导航图,始终向上到根 AnimatedNavHost。这意味着想要设置默认动画 (例如,穿插淡入淡出的机会),只须要在您的 AnimatedNavHost 中批改全局的 enterTransitionexitTransition

如果您只想批改某个子图的默认值 (例如,您的登录子图中的页面总是应用横向滑动动画),您也能够在嵌套图级别设置动画:

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") {// 增加内容}
}

请留神咱们应用 hierarchy 扩大办法.hierarchy()) 来判断某个目的地是否属于登录子图的一部分——这样一来,咱们 进入 登录子图和 来到 登录子图的过渡动画将应用默认值 (或者您在更高一级设置的任何过渡动画)。

每当您有一个方向性的过渡动画,比方程度滑动时,enterTransition 和 popEnterTransition 之间的区别就十分不便——您将可能防止造成一个页面向右滑动而另一个页面向左滑动的状况。

Accompanist 充当了 Jetpack 库的助推器,使得咱们能够在 Compose 1.1 的开发过程中立刻取得试验性功能。

增加 Accompanist 导航动画 依赖:

implementation "com.google.accompanist:accompanist-navigation-animation:0.16.0"

Navigation Compose 和动画的将来

随着基于 Compose 1.0.1 的 Navigation 2.4 和 Accompanist 导航动画库通过实验性 API 冲破了 Compose 1.0 的限度,还有其余内容行将展示: Compose 1.1。通过 Compose 路线图 能够发现,有一个十分重要的、令人兴奋的性能行将推出:

反对共享元素过渡

咱们对于 Navigation 2.5 的指标是将 Compose 1.1 的所有长处带到 Navigation Compose 中。这意味着当动画 API 解除实验性状态时,咱们能够间接将其带到 Navigation Compose。这也意味着咱们能够构建反对共享元素过渡的 API。

这还意味着 Accompanist 导航动画应该被视为一种长期措施: 一旦 Navigation Compose 本身提供了雷同级别的动画 API (依据您的反馈量身定做),您将能够间接依赖于它并且能够齐全移除 Accompanist 导航动画库。

继续前进

均衡稳定性以及咱们作为 Jetpack 库对本人提出的向前和向后兼容性要求,并具备疾速交付性能的能力,这并不像咱们设想的那么简略。随着 Jetpack Compose 一直倒退,对一直超前的需要而言,Accompanist 是一个微小的福音。我要感激 Chris Banes 和所有投入工夫在 Accompanist 上的开发者、Compose 背地的整个团队,以及大家帮忙塑造 Android 开发的将来。

备注 : 如果您正在寻找更多对于 Navigation+Accompanist 的材料,请查阅:

  • Accompanist Navigation Material
  • 介绍 Navigation-Material 🧭🎨️

欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!

退出移动版