androidandroid-fragmentsuinavigationbarbottomnavigationviewonsaveinstancestate

Save state of fragment A when navigating from bottomNavigationView to fragment B


I am new to Android and I have been struggling for some time now with saving state of a fragment. My app has 5 options in the bottomNavigationView (I am using the default activity from Android). In fragment A (actually called "SearchFragment") I have a button and when clicked it sets some text in a TextBox. I want to save the state of this fragment (with the text there) when navigating to another fragment and the coming back to the fragment A (and have the text still there from the previous button click).

From the code I wrote it saves the fragment's state when changing orientation for example, but doesn't do so when navigating to other fragments and then coming back.

How could I change the code? Or is there any other method to save the current state of a fragment on navigation? I really need some help here...and thanks for your attention!

My mainActivity looks like this:

    public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            BottomNavigationView navView = findViewById(R.id.nav_view);
            // Passing each menu ID as a set of Ids because each
            // menu should be considered as top level destinations.

            AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                    R.id.navigation_search, R.id.navigation_explore, R.id.navigation_trips, R.id.navigation_groups,R.id.navigation_profile)
                    .build();

            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
            NavigationUI.setupWithNavController(navView, navController);
        }

    }

And the fragment class looks like this:


    public class SearchFragment extends Fragment {

        private SearchViewModel searchViewModel;
        Button b;
        TextView text;
        Bundle savedState = null;

        public View onCreateView(@NonNull LayoutInflater inflater,
                                 ViewGroup container, Bundle savedInstanceState) {

            View root = inflater.inflate(R.layout.fragment_search, container, false);

            searchViewModel = ViewModelProviders.of(this).get(SearchViewModel.class);
            final TextView textView = root.findViewById(R.id.text_dashboard);
            searchViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
                @Override
                public void onChanged(@Nullable String s) {
                    textView.setText(s);
                }
            });
            Log.i("state","onCreate");
            Log.i("bundleIsNull", "" + (savedState == null));


            b  = root.findViewById(R.id.button2);
            text = root.findViewById(R.id.textView);


            b.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    text.setText("YEEEY");
                    Log.i("bundleIsNull", "" + (savedState == null));
                }
            });


            if(savedInstanceState != null && savedState == null) {
                savedState = savedInstanceState.getBundle("buttonText");
            }
            if(savedState != null) {
                text.setText(savedState.getCharSequence("buttonText"));
            }
            savedState = null;

            return root;
        }


        @Override
        public void onDestroyView() {
            super.onDestroyView();
            Log.i("state", "onDestroyView");
           savedState = saveState();
           text = null;
           b = null;
        }

        private Bundle saveState() {
            Bundle state = new Bundle();
            state.putCharSequence("buttonText", text.getText());
            return state;
        }

        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            Log.i("state", "onSaveInstanceState");
            outState.putBundle("buttonText", (savedState != null)? savedState : saveState());
        }

        @Override
        public void onPause() {
            super.onPause();
            Log.i("state","onPause");
        }
    }


Solution

  • Few facts around Android Navigation Component with Bottom Navigation Bar.

    - > Fragments are always recreated (onCreate, onViewCreated, onViewDestroyed are called as soon as the user navigates to another fragment)

    - > Fragment will save its state only when activity is recreated (e.g. screen rotation) , navigating between fragments doesn't save fragment's state.

    I want to save the state of this fragment (with the text there) when navigating to another fragment and the coming back to the fragment A (and have the text still there from the previous button click).

    This wont be possible as a new instance of FragmentA is created when you navigate back from fragmentB to Fragment A.

    you can not achieve this with navigation controller as of now.

    Since you are using navigation component, You should switch to using Viewmodel to solve retain fragment state issue.

    Consult the following link to see how to communicate and save data between fragments using viewmodel.

    https://androidwave.com/fragment-communication-using-viewmodel/