方向更改后每次重新创建片段,无法恢复状态

时间:2014-01-24 14:09:17

标签: android android-fragments android-configchanges

更新:事实证明问题来自其他地方。感谢@Luksprog指出我忽略了什么。

  1. 使用Android Studio的导航抽屉图案创建项目。抽屉在NavigationDrawerFragment类中实现。
  2. 当选择抽屉中的特定项目时,添加包含视图寻呼机的片段。该代码是在我的家庭活动中实现的。
  3. 当屏幕旋转时,调用onCreate() NavigationDrawerFragment方法,保留最后选择的项目。
  4. 这就是出错的地方 - 在娱乐时,NavigationDrawerFragment会再次调用selectItem(),这会触发我的菜单项选择处理程序。这会导致Android恢复ListFragment。
  5. 通过检查菜单选择处理程序代码中的活动菜单项可以防止这种情况。


    我希望在以任何原因重新创建活动时保留ViewPager的最后一个查看页面索引,例如方向改变。

    ViewPager位于Fragment(名为ListFragment),附加到某个活动。我正在使用compat库,因此片段是android.support.v4.app.Fragment的子类。

    我认为可以通过覆盖onSaveInstanceState()方法并在onCreate()中添加适当的逻辑来完成,如doc中所述:

      

    要正确处理重启,您的活动非常重要   通过正常的Activity生命周期恢复其先前的状态   Android在销毁之前调用onSaveInstanceState()   活动,以便您可以保存有关应用程序状态的数据。您   然后可以在onCreate()或期间恢复状态   onRestoreInstanceState()。

    但是片段的情况似乎有所不同。当我从此ListFragment导航到另一个活动并按下“返回”时,可以正确恢复页面索引。但是,当我旋转设备时,页面索引会丢失。

    我添加了一些日志记录,看看有什么问题。从日志中我发现尽管onSaveInstanceState() {我称之为ListFragment A'ListFragment被正确调用,但这个特定的Fragment类不再显示在活动中。当方向发生变化并重新创建活动时,Android会调用onSaveInstanceState(),然后调用onDetach()来分离此片段。然后 Android会创建一个ListFragment 的新实例(我将其命名为ListFragment B)并将其附加到新的旋转活动中。此ListFragment B有一个空savedInstanceState传递给构造函数,因此最后一个页面索引(以及片段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为空?如果这是预期的行为,那么解决我需求的正确方法是什么,即在配置发生变化时保持页面索引?

    应该可以使页面索引成为静态类变量,但我不确定它是否实际解决了问题,或者只是隐藏它(因为我在上面的日志中闻到了内存泄漏)。

5 个答案:

答案 0 :(得分:10)

即使答案已经被接受,让我更多地澄清一下:“问题”在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);
    }
}

刚刚失去半个生产日来解决这个问题。希望它可以帮助别人。

答案 1 :(得分:5)

在问题中已更新,这已得到解决,并再次感谢@Luksprog指出我忽略的内容。

Fragment的行为实际上与Activity类对齐。

以下是我的问题的原因:

  • 使用Android Studio的“创建项目”向导提供的导航抽屉模式创建项目。抽屉在NavigationDrawerFragment类中实现。
  • 当选择抽屉中的特定项目时,添加包含视图寻呼机的片段。该代码是在我的家庭活动中实现的。
  • 当屏幕旋转时,调用onCreate() NavigationDrawerFragment方法,保留最后选择的项目。
  • 这就是出错了 - 在重新开始时,NavigationDrawerFragment会再次调用selectItem(),这会触发我的菜单项选择处理程序。这会导致ListFragment被替换。

可以通过检查菜单选择处理程序代码中的活动菜单项或禁用selectItem()调用来防止这种情况。

答案 2 :(得分:1)

您不需要处理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();
            }
        }

答案 3 :(得分:0)

尝试在此使用onCreateView()代替onCreate(),这是特定于片段的,可能会有所作为。

覆盖onSaveInstanceState()以存储您的状态,并使用onCreateView()savedInstanceState.get*()中恢复该状态。这就是我们在应用程序中执行此操作的方式,它应该可以正常运行。正如@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);
            }
        }
    }
}

答案 4 :(得分:-2)

在AndroidManifest.xml中添加

<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize">
</activity>