Update: It turns out that the problem is from elsewhere. Thanks @Luksprog for pointing out what I overlooked.
NavigationDrawerFragment
class. onCreate()
method of NavigationDrawerFragment
is called, preserving last selected item.NavigationDrawerFragment
will call selectItem() again, which triggers my menu item selected handler. This causes the ListFragment restored by Android.This can be prevented by checking the active menu item in my menu selection handler code.
I want to retain the last viewing page index of the ViewPager
when the activity is recreated by whatever reason, e.g. orientation change.
The ViewPager
is in a Fragment
(named ListFragment
), which is attached to an activity. I am using the compat library, so the fragment is a subclass of android.support.v4.app.Fragment
.
I thought that it could be done by overriding the onSaveInstanceState()
method and add appropriate logic in onCreate()
, as metioned in the doc:
To properly handle a restart, it is important that your activity restores its previous state through the normal Activity lifecycle, in which Android calls onSaveInstanceState() before it destroys your activity so that you can save data about the application state. You can then restore the state during onCreate() or onRestoreInstanceState().
But the situation seems different for fragments. The page index can be correctly restored when I navigate from this ListFragment
to another activity and pressed "back". However when I rotate my device, the page index is lost.
I added some logging to see what's wrong. From the log I found that although onSaveInstanceState()
of the ListFragment
(I'll call it ListFragment A) is called properly, this particular Fragment class is no longer shown in the activity. When the orientation changed and the activity is recreated, Android calls onSaveInstanceState()
followed by onDetach()
to detach this fragment. Then Android creates a new instance of ListFragment
(I'll call it ListFragment B) and attach it to the new, rotated activity. This ListFragment B has an empty savedInstanceState
passed to the constructor, and thus the last page index (and any configuration in savedInstanceState of Fragment A) is lost.
In fact, a new instance of ListFragment
will be created every time a screen rotate occurs, but it seems that the old ones will not be destroyed. I see logs like below when I rotate the device:
D/ListFragment﹕ [1110257048] onSaveInstanceState() called, storing last page index 3
D/ListFragment﹕ [1109835992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108826176] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108083096] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1106541040] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108316656] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109134136] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108630992] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1108592888] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1109729064] onSaveInstanceState() called, storing last page index 0
D/ListFragment﹕ [1110257048] onDestroy()
D/ListFragment﹕ [1110257048] onDetach()
D/ListFragment﹕ [1109835992] onDestroy()
D/ListFragment﹕ [1109835992] onDetach()
D/ListFragment﹕ [1108826176] onDestroy()
D/ListFragment﹕ [1108826176] onDetach()
D/ListFragment﹕ [1108083096] onDestroy()
D/ListFragment﹕ [1108083096] onDetach()
D/ListFragment﹕ [1106541040] onDestroy()
D/ListFragment﹕ [1106541040] onDetach()
D/ListFragment﹕ [1108316656] onDestroy()
D/ListFragment﹕ [1108316656] onDetach()
D/ListFragment﹕ [1109134136] onDestroy()
D/ListFragment﹕ [1109134136] onDetach()
D/ListFragment﹕ [1108630992] onDestroy()
D/ListFragment﹕ [1108630992] onDetach()
D/ListFragment﹕ [1108592888] onDestroy()
D/ListFragment﹕ [1108592888] onDetach()
D/ListFragment﹕ [1109729064] onDestroy()
D/ListFragment﹕ [1109729064] onDetach()
D/ListFragment﹕ [1110903656] onAttach()
D/ListFragment﹕ [1110903656] onCreate()
D/ListFragment﹕ [1110903656] savedInstanceState is not NULL.
D/ListFragment﹕ [1110903656] Retrieving last page index 3
D/ListFragment﹕ [1110905248] onAttach()
D/ListFragment﹕ [1110905248] onCreate()
D/ListFragment﹕ [1110905248] savedInstanceState is not NULL.
D/ListFragment﹕ [1110905248] Retrieving last page index 0
D/ListFragment﹕ [1110906440] onAttach()
D/ListFragment﹕ [1110906440] onCreate()
D/ListFragment﹕ [1110906440] savedInstanceState is not NULL.
D/ListFragment﹕ [1110906440] Retrieving last page index 0
D/ListFragment﹕ [1110907632] onAttach()
D/ListFragment﹕ [1110907632] onCreate()
D/ListFragment﹕ [1110907632] savedInstanceState is not NULL.
D/ListFragment﹕ [1110907632] Retrieving last page index 0
D/ListFragment﹕ [1110908824] onAttach()
D/ListFragment﹕ [1110908824] onCreate()
D/ListFragment﹕ [1110908824] savedInstanceState is not NULL.
D/ListFragment﹕ [1110908824] Retrieving last page index 0
D/ListFragment﹕ [1110910016] onAttach()
D/ListFragment﹕ [1110910016] onCreate()
D/ListFragment﹕ [1110910016] savedInstanceState is not NULL.
D/ListFragment﹕ [1110910016] Retrieving last page index 0
D/ListFragment﹕ [1110911208] onAttach()
D/ListFragment﹕ [1110911208] onCreate()
D/ListFragment﹕ [1110911208] savedInstanceState is not NULL.
D/ListFragment﹕ [1110911208] Retrieving last page index 0
D/ListFragment﹕ [1110912400] onAttach()
D/ListFragment﹕ [1110912400] onCreate()
D/ListFragment﹕ [1110912400] savedInstanceState is not NULL.
D/ListFragment﹕ [1110912400] Retrieving last page index 0
D/ListFragment﹕ [1110913592] onAttach()
D/ListFragment﹕ [1110913592] onCreate()
D/ListFragment﹕ [1110913592] savedInstanceState is not NULL.
D/ListFragment﹕ [1110913592] Retrieving last page index 0
D/ListFragment﹕ [1110914784] onAttach()
D/ListFragment﹕ [1110914784] onCreate()
D/ListFragment﹕ [1110914784] savedInstanceState is not NULL.
D/ListFragment﹕ [1110914784] Retrieving last page index 0
D/HomeActivity﹕ fragment updated
D/ListFragment﹕ [1110914784] onCreateView()
D/ListFragment﹕ [1111031048] onAttach()
D/HomeActivity﹕ Fragment attached.
D/ListFragment﹕ [1111031048] onCreate()
D/ListFragment﹕ [1111031048] savedInstanceState is NULL.
D/ListFragment﹕ [1111031048] onCreateView()
D/ListFragment﹕ [1111031048] onResume(), restoring page index 0
This is the log after I rotated the screen for about 10 times. The number in the tag is the classes' hashCode()
. Above lines shows that onSaveInstanceState()
and onCreate()
of the previously created fragments still get called even after they are replaced by the latest (1111031048) one.
Note that I didn't call setRetainInstance()
in the fragment class. In fact, I tried both setRetainInstance(false)
and setRetainInstance(true)
but it doesn't change anything.
Did I do anything wrong here? I can understand that ListFragment
needs to be recreated, but why savedInstanceState
is null? And if this is the expected behavior, what is the correct way to solve my need, i.e. keeping the page index when configuration changes?
It should be possible to make the page index a static class variable, but I'm not sure if it is actually solving the issue, or just hiding it (because I smell memory leak in the log above).
Even though the answer has already been accepted, let me clarify this more: the "issue" is in the Android Studio template. The problem, as pointed out by Edmund, is in the Navigation Drawer calling the menu when recreated, thus re-calling the Fragment. To solve this, I made this slight modification on the NavigationDrawerFragment.java file as proposed by Android Studio. Original:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
}
// Select either the default item (0) or the last selected item.
selectItem(mCurrentSelectedPosition);
}
New one:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
} else {
// Select either the default item (0) or the last selected item.
selectItem(mCurrentSelectedPosition);
}
}
Just lost half a production day to figure this out. Hope it may help somebody else.
As updated in the question, this is resolved and thanks again to @Luksprog for pointing out what I overlooked.
The behavior of Fragment
actually aligns with Activity
classes.
Here is the cause of my issue:
NavigationDrawerFragment
class.onCreate()
method of NavigationDrawerFragment
is called, preserving last selected item.NavigationDrawerFragment
will call selectItem()
again, which triggers my menu item selected handler. This causes the ListFragment
be replaced.This can be prevented by checking the active menu item in my menu selection handler code, or by disabling that selectItem()
call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With