The Jetpack Navigation toggle causes redrawing

1. Phenomenon

Use Navigation to jump between fragments. When switching with the bottom Tab effect, fragments will refresh every time. This effect is certainly not what you want, so it needs to be solved.

2. Cause

Cause of the problem: when the Navigation group jumps to another Fragment, the Navigation () method uses the ft.replace() method in the source code
And mfragmentmanager getFragments(). Size () gets bigger and bigger.
Based on this, you should recreate a Fragment every time, not just refresh the page.

Since it's a new Fragment, it's not just a refresh problem. This is created through the full class name, and the analysis is likely to create a Fragment through reflection

Call create Fragment code. Through source code tracing, it is really reflection that creates a new source code Fragment
  public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
        try {
            Class<? extends Fragment> cls = loadFragmentClass(classLoader, className);
            return cls.getConstructor().newInstance();
}

3. Online solutions

Solution: copy the NavHostFragment and FragmentNavigator classes into the project, modify the navitgate() method in FragmentNavigator, and modify the path of import FragmentNavigator in NavHostFragment.

public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
 
// ... ellipsis
    if(mFragmentManager.getFragments().size()>0){                          
    ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1));
            ft.add(mContainerId, frag);
    }else {
            ft.replace(mContainerId, frag);
    }
//      ft.replace(mContainerId, frag);
    ft.setPrimaryNavigationFragment(frag);
 
// ... ellipsis
} 

This is the online source code, but direct use has no effect

The reason for the problem is that it is only changed here, or a new Fragment will be created every time, which can not achieve the expected effect

Modify again

···
if(mFragmentManager.getFragments().size()>0){
ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1));
ft.add(mContainerId, frag);
}else {
ft.replace(mContainerId, frag);
}
//This place should be revised according to the specific situation

//Here, first find out whether it has been loaded. If there is no add, if it has been loaded, it will be directly displayed, and then hide others
if(mFragmentManager.getFragments().size()>0){ // TODO
ft.hide(mFragmentManager.getFragments().get(mFragmentManager.getFragments().size()-1));
boolean hasAdd =false;
for (int i = 0; i < mFragmentManager.getFragments().size(); i++) {
Fragment f = mFragmentManager.getFragments().get(i);
if(f!=null&& f.getClass().getName().equals(className)){
hasAdd = true;
}else{
ft.hide (mFragmentManager.getFragments().get(i));
}
}
if(!hasAdd){
ft.add(mContainerId, frag);
}
// ft.show(frag);

        ft.replace(mContainerId, frag);
    }else {
        ft.replace(mContainerId, frag);
    }

···
After this location is changed, it still can't achieve the effect. That is, the source code above will create a new Fragment every time
Modify the part of the new source code

      Fragment tempFrag = null;
        for (int i = 0; i < mFragmentManager.getFragments().size(); i++) {
            Fragment  f = mFragmentManager.getFragments().get(i);
            if(f!=null&& f.getClass().getName().equals(className)){
                tempFrag = f;
            }
        }
        final Fragment frag  ;
        if(tempFrag!=null){
            frag = tempFrag;
        }else{
            frag  = instantiateFragment(mContext, mFragmentManager,  className, args);
        }
        frag.setArguments(args);

This line of code will first determine whether this fragment has been add ed before
If you add, you don't need to create a new one. Just display it

Overall process

Copy the NavHostFragment and FragmentNavigator classes into the project, modify the navitgate() method in FragmentNavigator, and modify the path of import FragmentNavigator in NavHostFragment

The copied file NavHostFragment is referenced in xml

<androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_nav_host_fragment"
        android:name="com.androideasy.navigate.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"  
        app:navGraph="@navigation/nav_graph_main" />

This temporarily realizes the switching effect
But I personally feel very bad. It is not recommended.

The modified file will be released to the personal Git and will not be released for the time being

contact information

Q group: 960244875 welcome to exchange and rest
Email: AndroidEasy@126.com

Keywords: Android

Added by Ark3typ3 on Fri, 04 Mar 2022 00:37:58 +0200