2

For a long time I have been investigating how to save the states between fragments so when I return it is as if you had never left. Google Photos is a key example. When you scroll through your photos and then go to another fragment and return you stay exactly where you were. My question is how can this be achieved since my application loads different fragments and when they return to these they are recreated instead of loading their previous state as it happens with Google Photos. What I have tried:


Part of the code where the nav_host_fragment is defined since it is navView

 appBarConfiguration = new AppBarConfiguration.Builder(
        R.id.navigation_home, R.id.navigation_profile, R.id.navigation_locate, R.id.navigation_contact_messages, R.id.navigation_settings)
        .build();

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

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

    navView.setOnNavigationItemReselectedListener(menuItem -> {

    });

    navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
        if (destination.getLabel() != null) {
            if (levelsFragment[0].equals(destination.getLabel().toString())) {
                //reemplazar por accion
            } else if (levelsFragment[1].equals(destination.getLabel().toString())) {
                //reemplazar por accion
            } else if (levelsFragment[2].equals(destination.getLabel().toString())) {
                //reemplazar por accion
            } else if (levelsFragment[3].equals(destination.getLabel().toString())) {
                //reemplazar por accion
            } else if (levelsFragment[4].equals(destination.getLabel().toString())) {
                //reemplazar por accion
            }
        }
    });

Xml of the MainActivity containing the bottomNavigation:

<androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/nav_view"
    style="@style/bottomNav"
    android:layout_width="0dp"
    app:itemTextColor="@color/quantum_grey"
    android:animateLayoutChanges="true"
    app:labelVisibilityMode="auto"
    app:itemIconTint="@color/colorAccent"
    android:layout_height="wrap_content"
    android:layout_marginStart="0dp"
    android:layout_marginEnd="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:menu="@menu/bottom_nav_menu" />

<fragment
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="?actionBarSize"
    app:defaultNavHost="true"
    app:layout_constraintBottom_toTopOf="@id/nav_view"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:navGraph="@navigation/mobile_navigation" />


</androidx.constraintlayout.widget.ConstraintLayout>

For example in itemReselected I left it blank because every time I browse it would create a new instance for me. The addOnDestinationChangedListener figured this is where saving the state goes. Any help would be appreciated. Here is the page where it is perfectly explained what I want to achieve:Material.io


Bottom navigation behaves differently on Android and iOS. When you select a bottom navigation item (one that’s not currently selected), each platform displays different outcomes:

  • On Android: the app navigates to a destination’s top-level screen. Any prior user interactions and temporary screen states are reset,
    such as scroll position, tab selection, and in-line search.
  • On iOS: the destination reflects the user’s prior interaction. If the user previously visited that section of the app, they return to the last screen viewed (with its prior state preserved, if possible). Otherwise, the app navigates to the top-level screen.

Default platform navigation can be overridden when needed to improve the user experience. For example, an Android app that requires frequent switching between sections can preserve each section’s state. Or, an iOS app can return users to the top-level screen (or reset their scroll position) if it better suits the use case.


This is an email from the Google Issue Tracker. Where my concern is clearly expressed and that in Google Photos it is done correctly. THERE HAS TO BE A SOLUTION TO THE PROBLEM

The two biggest issues and annoyances of this bug for our end users with the new Navigation library and Bottom navigation bar are:

  • Changing tab with the bottom bar resets the state and backstack of the previous tab/fragment, so our users are completely losing their navigation history (this is correctly implemented in Google Photos app)
  • Pressing back from the root of one tab shows the initial tab/fragment instead of leaving the app"

I found this by searching the internet but it is in kotlin and there are things that I cannot understand very well and I don't know if it is exactly what I need: Code in GitHub

Community
  • 1
  • 1
Alex Rivas
  • 161
  • 2
  • 9
  • an answer to that question is more valuable tnah 50 points but i havent enough sorry – Alex Rivas May 18 '20 at 17:28
  • Although, there are many answers [here](https://stackoverflow.com/questions/6834518/saving-fragment-state-in-viewpager) which can be used to save the state of Fragment or not destroy it so it doesn't reload when selected again but I believe [this](https://stackoverflow.com/a/16944679/8244632) is what help you the most. – Lalit Fauzdar May 25 '20 at 15:40
  • But, remember you need to manage the memory resources which you'll really need if your fragments would be going to stay open as high memory consumption can get your app killed by system memory manager and then fragment will reload again. – Lalit Fauzdar May 25 '20 at 15:41

1 Answers1

0

I dont know if there is a cleaner way to do this, but maybe you can try to save important stuff to recreate the view (like scroll position and maybe inline search) and then automatically scroll to the old position on re-open. It might also be possible to save the old instance (with scroll data, if that exists) and then when you switch back you copy the old Version back in instead of generating a new one. Be careful to copy the old-screen-object rather than setting a reference because it obviously gets deleted when the next one is generated..

Torge Rosendahl
  • 482
  • 6
  • 17
  • thanks for you answer but if you see in the question, material desgin says that the behavior can be override i want to know how to do that – Alex Rivas May 25 '20 at 16:09