簡體   English   中英

方向更改后每次重新創建片段,無法恢復狀態

[英]Fragment recreated every time after orientation change, unable to restore state

更新:事實證明問題來自其他地方。 感謝@Luksprog指出我忽略了什么。

  1. 該項目是使用Android Studio的導航抽屜圖案創建的。 抽屜在NavigationDrawerFragment類中實現。
  2. 當選擇抽屜中的特定項目時,添加包含視圖尋呼機的片段。 代碼實現了我的家庭活動。
  3. 當屏幕旋轉時,將調用NavigationDrawerFragmentonCreate()方法,保留最后選擇的項目。
  4. 這里出了什么問題 - 在娛樂時, 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類對齊。

以下是我的問題的原因:

  • 該項目使用Android Studio的“創建項目”向導提供的導航抽屜模式創建。 抽屜在NavigationDrawerFragment類中實現。
  • 當選擇抽屜中的特定項目時,添加包含視圖尋呼機的片段。 代碼實現了我的家庭活動。
  • 當屏幕旋轉時,將調用NavigationDrawerFragmentonCreate()方法,保留最后選擇的項目。
  • 這里出了什么問題 - 在娛樂時, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM