1, Emerging phenomena
Use Navigation to jump between fragments. When you go back, you find that the data on the previous Fragment page is gone. After investigation, it is found that the page has been re executed onCreateView, that is, the page has been re created. In this way, the experience effect is different from that of previous multiple activities.
2, Analyze the reason
In fact, this is deliberately designed by google. google's original intention is to have a ViewModel in the host Activity. The ViewModel maintains the data in the page. Every time a Fragment is created, it obtains the previous data through the ViewModel. If the page is a ListView, how to maintain the previous state when re creating is more troublesome. So what can be done?
3, Solution
First of all, we need to know why Fragment executes onCreateView when it returns. After analysis, the key lies in the navigate method in the class FragmentNavigator
@Nullable @Override public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { //Ignore partial code if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) { enterAnim = enterAnim != -1 ? enterAnim : 0; exitAnim = exitAnim != -1 ? exitAnim : 0; popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0; popExitAnim = popExitAnim != -1 ? popExitAnim : 0; ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim); } //The key is this replace, which will replace the current page every time a new page is opened ft.replace(mContainerId, frag); ft.setPrimaryNavigationFragment(frag); final @IdRes int destId = destination.getId(); final boolean initialNavigation = mBackStack.isEmpty(); // TODO Build first class singleTop behavior for fragments final boolean isSingleTopReplacement = navOptions != null && !initialNavigation && navOptions.shouldLaunchSingleTop() && mBackStack.peekLast() == destId; //Ignore partial code }
After viewing the source code, we can actually replace the current page with the new page opened. We know that we can hide the current page first and then add a new page. However, we can't modify the source code, because we can copy a copy of our own modification and use the class we created when referencing.
We copy the FragmentNavigator class, rename a MyFragmentNavigator, and modify the navigate method
@Nullable @Override public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) { //Ignore partial code if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) { enterAnim = enterAnim != -1 ? enterAnim : 0; exitAnim = exitAnim != -1 ? exitAnim : 0; popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0; popExitAnim = popExitAnim != -1 ? popExitAnim : 0; ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim); } //First hide the current fragment, and then add a new page if(mFragmentManager.getFragments().size()>0){ ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1)); ft.add(mContainerId, frag); }else { ft.replace(mContainerId, frag); } ft.setPrimaryNavigationFragment(frag); final @IdRes int destId = destination.getId(); final boolean initialNavigation = mBackStack.isEmpty(); // TODO Build first class singleTop behavior for fragments final boolean isSingleTopReplacement = navOptions != null && !initialNavigation && navOptions.shouldLaunchSingleTop() && mBackStack.peekLast() == destId; //Ignore partial code }
At present, we have created MyFragmentNavigator, but this MyFragmentNavigator cannot be used directly. Through source code analysis, we know that NavHostFragment references FragmentNavigator, so we must also modify NavHostFragment. We only need to copy a NavHostFragment, rename it MyNavHostFragment, and change the referenced FragmentNavigator into our own MyFragmentNavigator.
4, How to use
We know it is written like this in the Activity layout file
<fragment app:defaultNavHost="true" app:navGraph="@navigation/my_nav" android:id="@+id/nav_host_fragment" //Just change this to the full path of MyNavHostFragment android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent"> </fragment>
Then we can use it normally. The jump and parameter transfer of the page use the previous code
Summary:
1. Copy the FragmentNavigator to MyFragmentNavigator and modify the navigate method inside
2. Copy NavHostFragment to MyNavHostFragment, and change the internally referenced FragmentNavigator to MyFragmentNavigator
3. Replace the full path of MyNavHostFragment in the main Activity layout file