Mehmet Ceylan
Mehmet Ceylan

Reputation: 395

Navigation Architecture Component - OnBackPressed() - Android

I progress in fragments with the Android navigation architecture.

When I press the key on the last screen or when I put a button on the screen and set the button function as

val navıgattor = activity.findNavController(R.id.nav_host_fragment)
navigator.popBackStack()

and click, I return to the previous fragment, but the onCreateView () method in the previous fragment is called again. I was expecting it to keep its state in normal behavior.

Where am I doing wrong?

Upvotes: 1

Views: 1692

Answers (3)

navid
navid

Reputation: 1398

From fragment documentation:

/**
 * Called when the view previously created by {@link #onCreateView} has
 * been detached from the fragment.  The next time the fragment needs
 * to be displayed, a new view will be created.  This is called
 * after {@link #onStop()} and before {@link #onDestroy()}.  It is called
 * <em>regardless</em> of whether {@link #onCreateView} returned a
 * non-null view.  Internally it is called after the view's state has
 * been saved but before it has been removed from its parent.
 */
public void onDestroyView()

so whenever onDestroyView is called (for whatever reason) you will see onCreateView called when the fragment is back to the top. For regaining the state of the view there are some tools to consider (you decide if which one or what mixture of them is suitable for you):

1- Overriding onSaveInstanceState, passing current state as bundle and reusing it when onCreateView is called by the android system with the bundle as parameter.

2- Using a ViewModel containing view state info, which is bonded with Either the Fragment, its View or its parent Activity and is destroyed along with such.

3- Keeping a copy of the View object being returned by onCreateView inside the fragment and reusing it if not null when onCreateView is called again (use with caution as some views might find troubles redrawing, for example OSM MapView):

private var viewCopy : View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    if(viewCopy!=null)
        return viewCopy
    ...
    viewCopy = inflatedView
    return viewCopy
}

Upvotes: 0

Nikhil Sharma
Nikhil Sharma

Reputation: 847

I will answer into two parts -

Solution:

Do not inflate view every time you are coming back to previous fragment. Save View in a local variable and inflate it only once. Suggested by Ian Lake here

private var savedViewInstance: View? = null

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return if (savedViewInstance != null) {
        savedViewInstance
    } else {
        savedViewInstance =
                inflater.inflate(R.layout.fragment_professional_details, container, false)
        savedViewInstance
    }
}

Explanation

The behavior you are getting is THE DEFAULT behavior, fragment will recreate their view each time you call navigator.popBackStack() or use back button on device.

Let's understand life cycle of a fragment under navigation architecture.

Scenario: We are taking two fragments, HomeFragment and DashboardFragment. Both fragments belong to same NavGraph and start destination is Home Fragment.

Fragment Life Cycle on launching of app-

HomeFragment: onAttach:

HomeFragment: onCreate:

HomeFragment: onCreateView:

HomeFragment: onViewCreated:

HomeFragment: onActivityCreated:

HomeFragment: onStart:

HomeFragment: onResume:

On Navigation: Home Fragment ---> Dashboard Fragment

DashboardFragment: onAttach:

DashboardFragment: onCreate:

DashboardFragment: onCreateView:

DashboardFragment: onViewCreated:

DashboardFragment: onActivityCreated:

DashboardFragment: onStart:

DashboardFragment: onResume:

HomeFragment: onPause:

HomeFragment: onStop:

HomeFragment: onDestroyView:

On Navigation: Dashboard Fragment ---> Home Fragment

HomeFragment: onAttach:

HomeFragment: onCreate:

HomeFragment: onCreateView:

HomeFragment: onViewCreated:

HomeFragment: onActivityCreated:

HomeFragment: onStart:

HomeFragment: onResume:

DashboardFragment: onPause:

DashboardFragment: onStop:

HomeFragment: onDestroy:

DashboardFragment: onDestroyView:

DashboardFragment: onDestroy:

If we are saving view on intial HomeFragment: onCreateView() and inflating same view every time for next call of HomeFragment: onCreateView(), we can get old view restored.

If you notice HomeFragment: onDestroy() will be called but after HomeFragment: onViewCreated() has called. Calling of HomeFragment: onDestroy() is just destroying old instance of HomeFragment.

I still believe this way of doing things are not best practice but it will be until, Google will come up something like onFragemntRestore().

On Another hand Fragments are suppose to be recreated every time they are removed or replaced and you are suppose to restore there states using onSaveInstanceState().

Here come the ViewModel to save the hustle of saving fragment state and restoring them. To actually update the view, you must need to ViewModel and observe the changes to change in the views.

enter image description here In simple words, If you have something which is taking care of data for your views no matter where you are, if you back on same position without any changes, that something has the information about how you were looking earlier. That something is ViewModel.

There are many other worth reading on same topic like this, this and this

Happy Coding !

Upvotes: 5

Kristijan Zrno
Kristijan Zrno

Reputation: 116

You are not doing anything wrong, this behaviour is normal for fragments. When you are navigating to another fragment, the fragment manager only saves the transaction without saving the state of the current fragment.

To save the instance state of a fragment, you can override the onSaveInstanceState() function and put the data in the out-state bundle. To retreive the saved data, you can override the onActivityCreated() and check if the data is contained within the savedInstanceState bundle.

You can check out the fragment lifecycle here:

enter image description here

Upvotes: 2

Related Questions