[英]Fragment recreated every time after orientation change, unable to restore state
更新:事實證明問題來自其他地方。 感謝@Luksprog指出我忽略了什么。
NavigationDrawerFragment
類中實現。 NavigationDrawerFragment
的onCreate()
方法,保留最后選擇的項目。 NavigationDrawerFragment
將再次調用selectItem(),這會觸發我的菜單項選擇處理程序。 這會導致Android恢復ListFragment。 通過檢查菜單選擇處理程序代碼中的活動菜單項可以防止這種情況。
我想保留ViewPager
的最后一個查看頁面索引,當任何原因重新創建活動時,例如方向更改。
ViewPager
位於Fragment
(名為ListFragment
)中,該Fragment
附加到活動。 我正在使用compat庫,因此該片段是android.support.v4.app.Fragment
的子類。
我認為可以通過重寫onSaveInstanceState()
方法並在onCreate()
添加適當的邏輯來完成,如文檔中所述:
要正確處理重新啟動,您的活動必須通過正常的Activity生命周期恢復其先前的狀態,在此生命周期中Android會在銷毀活動之前調用onSaveInstanceState(),以便您可以保存有關應用程序狀態的數據。 然后,您可以在onCreate()或onRestoreInstanceState()期間恢復狀態。
但是片段的情況似乎不同。 當我從這個ListFragment
導航到另一個活動並按下“返回”時,可以正確恢復頁面索引。 但是,當我旋轉設備時,頁面索引會丟失。
我添加了一些日志記錄,看看有什么問題。 從日志中我發現雖然ListFragment
onSaveInstanceState()
(我稱之為ListFragment A)被正確調用,但這個特定的Fragment類不再顯示在activity中。 當方向改變並重新創建活動時,Android會調用onSaveInstanceState()
然后調用onDetach()
來分離此片段。 然后Android創建一個新的ListFragment
實例 (我將其命名為ListFragment B)並將其附加到新的旋轉活動。 此ListFragment B具有傳遞給構造函數的空savedInstanceState
,因此最后一個頁面索引(以及Fragment A的savedInstanceState中的任何配置)都將丟失。
實際上,每次屏幕旋轉時都會創建一個新的ListFragment
實例,但似乎舊的實例不會被銷毀。 我在旋轉設備時會看到如下所示的日志:
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
這是我旋轉屏幕大約10次后的日志。 標記中的數字是類的hashCode()
。 上面的行顯示,先前創建的片段的onSaveInstanceState()
和onCreate()
即使在被最新的(1111031048)替換之后仍然會被調用。
請注意,我沒有在片段類中調用setRetainInstance()
。 實際上,我嘗試了setRetainInstance(false)
和setRetainInstance(true)
但它沒有改變任何東西。
我這里做錯了嗎? 我可以理解, ListFragment
需要重新創建,但為什么savedInstanceState
為空? 如果這是預期的行為,那么解決我需求的正確方法是什么,即在配置更改時保持頁面索引?
應該可以使頁面索引成為靜態類變量,但我不確定它是否實際解決了問題,或者只是隱藏它(因為我在上面的日志中聞到了內存泄漏)。
盡管答案已經被接受,但我要澄清一點:“問題”在Android Studio模板中。 正如Edmund所指出的那樣,問題在於導航抽屜在重新創建時調用菜單,從而重新調用片段。 為了解決這個問題,我對Android Studio提出的NavigationDrawerFragment.java文件做了一些修改。 原版的:
@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);
}
新的一個:
@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);
}
}
剛剛失去半個生產日來解決這個問題。 希望它可以幫助別人。
在問題中更新,這已得到解決,並再次感謝@Luksprog指出我忽略了什么。
Fragment
的行為實際上與Activity
類對齊。
以下是我的問題的原因:
NavigationDrawerFragment
類中實現。 NavigationDrawerFragment
的onCreate()
方法,保留最后選擇的項目。 NavigationDrawerFragment
將再次調用selectItem()
,這會觸發我的菜單項選擇處理程序。 這會導致ListFragment
被替換。 通過檢查菜單選擇處理程序代碼中的活動菜單項,或禁用該selectItem()
調用,可以防止這種情況。
您不需要處理savedInstanceState。 因為存儲在片段管理器中的所有片段(它像片段數組)以及當方向改變活動時會破壞,但片段管理器仍然保留之前添加的所有片段。 所以你只需要檢查片段是否已經添加。
private void openFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment existingFragment = fragmentManager.findFragmentByTag(tag);
if (existingFragment != null) {
fragmentTransaction.add(R.id.container, fragment, tag);
fragmentTransaction.commit();
}
}
嘗試在這里使用onCreateView()
而不是onCreate()
,它特定於Fragments並且可能會有所不同。
覆蓋onSaveInstanceState()
以存儲您的狀態,並使用savedInstanceState.get*()
在onCreateView()
恢復它。 這就是我們在應用程序中執行此操作的方式,它應該可以正常運行。 正如@Ari所提到的,請確保調用super.onSaveInstanceState(outState)
。
class MyFragment extends ListFragment {
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(ARG_PAGE, page);
super.onSaveInstanceState(outState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(savedInstanceState != null){
if(savedInstanceState.containsKey(ARG_PAGE)){
page = savedInstanceState.getInteger(ARG_PAGE);
}
}
}
}
在AndroidManifest.xml中添加
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize">
</activity>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.