current position:Home>Using navigation components: conditional navigation | mad skills

Using navigation components: conditional navigation | mad skills

2022-01-26 23:41:14 Android_ developer

This is the second one about navigation (Navigation) Of MAD Skills series , This is the second article in the navigation components series , If you want to look back on past releases , Please refer to the link below to see :

If you prefer to watch videos rather than read articles , Please check out This video Content .

summary

Conditional navigation (Conditional navigation) When designing navigation for an application , You may need to move users to one destination rather than another based on conditional logic . for example , Users may follow deep links to a destination that requires users to log in , Or you may provide different destinations for players to win or lose in the game .

stay Last article in , I use NavigationUI The bottom navigation of the application is realized , And increased. SelectionFragment To enable or disable the coffee recording function . However , Whether we disable or enable the coffee recorder , Users can navigate to CoffeeList Fragment page , This doesn't seem logical .

In this paper , I'll fix this by adding conditional navigation , And guide our users to make choices when they first enable the application . I will use Datastore API To save the user's selection , And decide whether to display... In the bottom navigation coffeeList Destination .

Preparations for using conditional navigation in applications

This is what I have done since the last article modify A quick review of :

If you want to see the specific changes , Please check Read the warehouse . If you follow the article , You can also check out the code in the warehouse .

Now the application has 3 Different states :

  • DONUT_ONLY: It means that the user has disabled the coffee recording function
  • DONUT_AND_COFFEE: It means that users want to record the consumption of doughnuts and coffee at the same time
  • NOT_SELECTED: It means that the user has not made a choice yet, and it may be the first time to start the application , Or it may be difficult for users to make decisions

Realize conditional navigation

I will be in SelectionFragment Start implementing conditional navigation in . First, I got SelectionViewModel An example of , So I can access it DataStore. then , I observe (Observe) The user's selection is and used to restore the state of the check box . In order to save the user's selection , I'll call... When the check box is clicked saveCoffeeTrackerSelection() To update the status .

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)
   //...
 Copy code 

Now it's time to update the bottom tab bar according to the user's choice . If the user chooses to disable coffee recording , There is only one left in the bottom tab bar donutList Options. , This means that we can safely remove the bottom tab bar . stay MainActivity in , I'll add an observer (Observer) And update the visibility of the bottom tab bar (Visibility). To achieve this , I'll add an observer and update it according to the user's choice BottomNavigation The visibility of .

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
   }
}
 Copy code 

stay onCreate() in :

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

Run the application in the current state , You will find that enabling or disabling coffee records will add or remove the bottom tab bar in the application accordingly . It looks great , But if we automatically send it to the user for selection when the user runs the application for the first time , That would be better .

DonutList By default Fragment, It is also our starting destination , This means that applications always start from DonutList start-up , I will check whether the user has made a choice before , without , Trigger navigation to SelectionFragment.

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

Before testing this function , I need to uninstall the app from the device , To ensure that preferences left over from the last run are not saved . Now when I run the application , It will navigate to SelectionFragment. The launch of subsequent applications will remember my choices and navigate me to the correct starting destination .

That's all. ! We are DonutTracker Conditional navigation has been added to the application . But how do we test the process ? Uninstalling the application or deleting the application data before running the test is not the best effect . This is the test (Testing) The problem to be solved !

Test navigation

I am here androidTest A folder named OneTimeFlowTest The test class . And then I created one called testFirstRun() The test method of , And add @Test annotation . Now I begin to implement the test . I use applicationContext Created TestNavHostController(), I'm also for the newly created testNavigationController The instance sets the... In the application nav_graph.

@Test
fun testFirstRun() {
   //  Create simulated  NavController
   val mockNavController = TestNavHostController(
       ApplicationProvider.getApplicationContext()
   )
   mockNavController.setGraph(R.navigation.nav_graph)
   //...
}
 Copy code 

thus ,mockNavigationController It's ready to use , Now it's time to create test scenarios . Do that , I use DonutList Fragment Start the app and set up what I created before mockNavigationController example . Then check to see if the application automatically navigates to... As expected 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)
}
 Copy code 

Now I run the test and wait for the results ... The test passed !

△  Test navigation

△ Test navigation

Summary

In this paper , I am here DonutTracker Conditional navigation has been added to the application , Tests have also been added to verify that the process is working properly —— Solution code .

Navigate through conditions , When the user first starts DonutTracker When applied , The application will trigger a process , Navigate the user to SelectionFragment. If the user chooses to disable the coffee recorder , The app will remove the coffee list from the navigation menu (CoffeeList).

thus , The coffee recording function is complete ! In the following article , We'll learn how to use nested graphs (Nested graphs) And modularize the application .

copyright notice
author[Android_ developer],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201262341106482.html

Random recommended