关于android:使用导航组件-条件导航-MAD-Skills

58次阅读

共计 3929 个字符,预计需要花费 10 分钟才能阅读完成。

这是第二个对于导航 (Navigation) 的 MAD Skills 系列,本文是导航组件系列的第二篇文章,如果您想回顾过去公布的内容,请参考上面链接查看:

  • 导航组件概览
  • 导航到对话框
  • 在利用中导航时应用 SafeArgs
  • 应用深层链接导航
  • 打造您的首个 app bundle
  • 深入浅出 NavigationUI

如果您更偏向于观看视频而非阅读文章,请查看 这个视频 内容。

概述

条件导航 (Conditional navigation) 指的是在为利用设计导航时,您可能须要基于条件逻辑将用户转到某一个目的地而非另一个。例如,用户可能会追随深层链接返回一个须要用户登录的目的地,或者您可能会在游戏中针对玩家的输赢提供不同的目的地。

在 上一篇文章 中,我应用 NavigationUI 实现了利用的底部导航,并减少了 SelectionFragment 来启用或禁用咖啡记录性能。然而,无论咱们禁用或启用咖啡记录器,用户都能够导航到 CoffeeList Fragment 页面,这看起来不太合乎逻辑。

在本文中,我将通过增加条件导航来修复这个问题,并且当用户首次启用利用时领导咱们的用户做出抉择。我将应用 Datastore API 来保留用户的抉择,并据此决定是否在底部导航中展现 coffeeList 目的地。

在利用中应用条件导航的筹备工作

这是自上一篇文章以来我所做 批改 的疾速回顾:

  • 首先,我增加了 UserPreferencesRepository,它应用 DataStore API 来保留用户的抉择;
  • 为了拜访该 Repository,我在各 ViewModel 工厂类中也做出了些许扭转,并且批改了 DonutListViewModel 和 SelectionViewModel 的结构形式。

如果您想查看具体的批改内容,请查 阅该仓库。如果您跟着文章一起操作,也能够检出仓库中的代码。

当初利用具备 3 种不同的状态:

  • DONUT_ONLY: 意味着用户禁用了咖啡记录性能
  • DONUT_AND_COFFEE: 意味着用户想同时记录甜甜圈和咖啡的生产状况
  • NOT_SELECTED: 意味着用户还没有做出抉择而且有可能是第一次启动利用, 或者用户兴许很难做出决定 🤷

实现条件导航

我将在 SelectionFragment 中开始实现条件导航。首先我获取了 SelectionViewModel 的一个实例,因而我能够通过它拜访 DataStore。而后,我察看 (Observe) 了用户的抉择并以此来复原复选框的状态。为了保留用户的抉择,我将在复选框被点击时调用 saveCoffeeTrackerSelection() 来更新状态。

val selectionViewModel: SelectionViewModel by viewModels {
   SelectionViewModelFactory(UserPreferencesRepository.getInstance(requireContext())
   )
}
selectionViewModel.checkCoffeeTrackerEnabled().observe(viewLifecycleOwner) { selection ->
   if (selection == UserPrefRepository.Selection.DONUT_AND_COFFEE){binding.checkBox.isChecked = true}
}
binding.button.setOnClickListener { button ->
   val coffeeSelected = binding.checkBox.isChecked               
   selectionViewModel.saveCoffeeTrackerSelection(coffeeSelected)
   //...

当初是时候依据用户的抉择来更新底部标签栏了。如果用户抉择禁用咖啡记录,底部标签栏中便只剩下一个 donutList 选项了,这意味着咱们能够平安的移除底部标签栏。在 MainActivity 中,我将增加观察者 (Observer) 并且更新底部标签栏的可见性 (Visibility)。为了实现这一目标,我将增加一个观察者并且依据用户的抉择来更新 BottomNavigation 的可见性。

private fun setupMenu(selection: UserPreferencesRepository.Selection) {val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
   bottomNav.isVisible = when (selection) {
       UserPreferencesRepository.Selection.DONUT_AND_COFFEE -> true
       else -> false
   }
}

在 onCreate() 中:

val selectionViewModel: SelectionViewModel by viewModels {
   SelectionViewModelFactory(UserPreferencesRepository.getInstance(this)
   )
}
selectionViewModel.checkCoffeeTrackerEnabled().observe(this) { s ->
   setupMenu(s)
}

在以后状态下运行利用,您会发现启用或禁用咖啡记录将对应地在利用中增加或移除底部标签栏。这看起来很棒,然而如果咱们在用户首次运行利用时主动将其发送给用户进行抉择,那会更好。

DonutList 是默认的 Fragment,也是咱们的起始目的地,这意味着利用总是从 DonutList 启动,我会检查用户之前是否做出过抉择,如果没有,则触发导航至 SelectionFragment

donutListViewModel.isFirstRun().observe(viewLifecycleOwner) { s ->
   if (s == UserPreferencesRepository.Selection.NOT_SELECTED) {val navController = findNavController()
       navController.navigate(DonutListDirections.actionDonutListToSelectionFragment()
       )
   }
}

在测试该性能前,我须要从设施上卸载利用,以确保不会保留上次运行时遗留下的偏好设置。当初当我运行利用时,它会导航至 SelectionFragment。后续利用的启动将会记住我做出的抉择并将我导航至正确的起始目的地。

就是如此!咱们在 DonutTracker 利用中增加了条件导航。然而咱们如何测试该流程?每次运行测试前都卸载利用或删除利用数据的话并不是最现实的成果。这就是测试 (Testing) 所要解决的问题!

测试导航

我在 androidTest 文件夹下创立了一个名为 OneTimeFlowTest 的测试类。而后我创立了一个名为 testFirstRun() 的测试方法,并为它增加 @Test 注解。当初我开始实现该测试。我应用 applicationContext 创立了 TestNavHostController(),我也为刚创立的 testNavigationController 实例设置了利用中的 nav_graph

@Test
fun testFirstRun() {
   // 创立模仿的 NavController
   val mockNavController = TestNavHostController(ApplicationProvider.getApplicationContext()
   )
   mockNavController.setGraph(R.navigation.nav_graph)
   //...
}

至此,mockNavigationController 曾经能够应用了,当初是创立测试场景的时候了。要做到这一点,我用 DonutList Fragment 启动利用并设置我之前创立的 mockNavigationController 实例。而后查看利用是否像预期那样主动导航至 SelectionFragment

val scenario = launchFragmentInContainer {DonutList().also { fragment ->
       fragment.viewLifecycleOwnerLiveData.observeForever{  
           viewLifecycleOwner ->
           if (viewLifecycleOwner != null){
               Navigation.setViewNavController(fragment.requireView(),
                   mockNavController
               )
           }
       }
   }
}
scenario.onFragment {
   assertThat(mockNavController.currentDestination?.id).isEqualTo(R.id.selectionFragment)
}

当初我运行该测试并期待后果 … 测试顺利通过!

△ 测试导航

小结

在本文中,我在 DonutTracker 利用中增加了条件导航,同时也增加了测试来验证流程是否失常工作——解决方案代码。

通过条件导航,当用户首次启动 DonutTracker 利用时,利用将触发一次流程,将用户导航至 SelectionFragment。如果用户抉择禁用咖啡记录器,利用将从导航菜单中移除咖啡列表 (CoffeeList)。

至此,咖啡记录性能曾经残缺了!在接下来的文章中,咱们将学习如何应用嵌套图 (Nested graphs) 并将模块化该利用。

正文完
 0