在FragmentTransaction.replace()和后退按钮之后,viewpage片段不会被重新创建



我试图实现保存和恢复状态,但是当用PreferenceFragment替换主Fragment然后点击后退按钮时,我遇到了问题。我的主要FragmentViewPagerFragmentPagerAdapter组成,Fragments可以滑动通过。在点击后退按钮后,我的3个FragmentFragment.onCreateView()回调都没有被调用。我已经尝试了所有的解决方案,但我还是没能解决这个问题。

另一个可能需要注意的重要事情是,我的3个ViewPager Fragment的数据存储在单独的类中,这些类是实例化的,可以通过Activity访问。3个Fragment s都包含RecyclerView s,用于列出相当数量的数据。这样做是为了清洁,但也是为了使这些数据保存在Activity中。这是可能不是一个问题,因为它在启动应用程序时工作正常,但也因为主要问题是我的Fragments没有重新创建。

意想不到的行为:

在应用程序创建时,一切工作正常,但是当我用另一个替换我的主Fragment(包含ViewPagerFragmentPagerAdapter)然后按后退按钮时,ViewPager中的Fragment s没有重新创建。我的主Fragment onCreateView()被称为

问题:

我错过了什么?是否应该创建一些其他回调?我应该在哪里以及如何重建Fragments ?

What have I try:

  1. 改变了FragmentAdapter的使用,但我应该真正使用getSupportFragmentManager()作为这里的解决方案。

编辑:使用错误的FragmentManager实际上我的麻烦的来源。请看下面我的回答。

  • 添加@Override public int getItemPosition(Object object) {return POSITION_NONE;}FragmentPagerAdapter,如这里所建议的。
  • FragmentPagerAdapterFragmentStatePagerAdapter的变化在这里应该无关紧要,据我所知。FragmentStatePagerAdapter的内部实际上保留了Fragments,但它们不会重新渲染。
  • onCreateView()被调用时,使用FragmentTransaction添加主Fragment,在Fragment的其他回调中,参见下面的MMMainFragment
  • 各种各样的东西。
  • 我的MainFragment类与相应的XML布局

    public class MMMainFragment extends Fragment
    {
        private MMViewPager mMMViewPager = null;
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            // Note that this method IS called when the back button is pressed.
            // I have tried setting the content back to this instance in several places.
            View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
            // Setup ViewPager.
            mMMViewPager = (MMViewPager) view.findViewById(R.id.mm_pager);
            TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
            tabLayout.setupWithViewPager(mMMViewPager);
            return view;
        }
        public MMViewPager getMMViewPager()
        {
            return mMMViewPager;
        }
    }
    

    mm_main_fragment.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <android.support.design.widget.TabLayout
            android:id="@+id/mm_tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="scrollable" />
        <com.mycompany.myapp.gui.mmpager.MMViewPager
            android:id="@+id/mm_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1" />
    
    </LinearLayout>
    

    然后,在Activity.onCreate()中我简单地做:

    // Insert Main Fragment.
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.mm_content_frame, new MMMainFragment())
            .commit();
    

    其中mm_content_frameFrameLayout,我替换和删除Fragment s。然后,当按下查看首选项的按钮时,我在Activity中运行下面的代码片段。我将这个Fragment添加到backstack中,以便能够使用后退按钮。

    public void showSettingsFragment()
    {
        getSupportActionBar().setTitle(getString(R.string.mm_drawer_settings));
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.mm_content_frame, new MMPreferencesFragment(), FragmentConstants.SETTINGS_FRAGMENT_TAG)
                .addToBackStack(null)
                .commit();
    }
    

    MMViewPager类:

    public class MMViewPager extends ViewPager
    {
        private MMActivity mMMActivity;
        private MMPagerAdapter mMMPagerAdapter;
        public MMViewPager(Context context, AttributeSet attrs)
        {
            super(context, attrs);
            mMMActivity = (MMActivity) context;
            mMMPagerAdapter = new MMPagerAdapter(mMMActivity.getSupportFragmentManager(), mMMActivity);
            this.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
            this.setAdapter(mMMMPagerAdapter);
            this.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
        }
    }
    

    MMPagerAdapter类,当前为FragmentStatePagerAdapter:

    public class MMPagerAdapter extends FragmentStatePagerAdapter
    {
        private MMActivity mMMActivity;
        public MMPagerAdapter(FragmentManager fragmentManager, MMActivity mmActivity)
        {
            super(fragmentManager);
            mMMActivity = mmActivity;
        }
        @Override
        public Fragment getItem(int position)
        {
            switch (position)
            {
                case PagerConstants.PAGE_FILTER_RECIPES:
                    return new FilteredRecipesFragment();
                case PagerConstants.PAGE_SELECTED_RECIPES:
                    return new SelectedRecipesFragment();
                case PagerConstants.PAGE_SHOPPING_LIST:
                    return new ShoppingListFragment();
                default:
                    return null;
            }
        }
        @Override
        public int getCount()
        {
            return PagerConstants.NUMBER_OF_PAGES; // 3
        }
        @Override
        public CharSequence getPageTitle(int position)
        {
            switch (position)
            {
                case PagerConstants.PAGE_FILTER_RECIPES:
                    return mMMActivity.getResources().getString(R.string.mm_title_recipe_filter_fragment);
                case PagerConstants.PAGE_SELECTED_RECIPES:
                    return mMMActivity.getResources().getString(R.string.mm_title_selected_recipes_fragment);
                case PagerConstants.PAGE_SHOPPING_LIST:
                    return mMMActivity.getResources().getString(R.string.mm_title_shopping_list_fragment);
                default:
                    return "Tab";
            }
        }
    }
    

    作为参考,这里也是Activity的主要布局:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/mm_drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!-- Source: http://developer.android.com/training/implementing-navigation/nav-drawer.html -->
        <!-- The main content view -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!-- Source: http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html -->
            <android.support.v7.widget.Toolbar
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/mm_toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="?attr/actionBarSize"
                android:layout_alignParentTop="true"
                style="@style/MMActionBar"
                app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
            <!-- The FrameLayout where I replace Fragments -->
            <FrameLayout
                android:id="@+id/mm_content_frame"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@id/mm_toolbar"/>
        </RelativeLayout>
    
        <ListView
            android:id="@+id/mm_drawer_listview"
            android:layout_width="260dp"
            android:layout_height="match_parent"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingStart="16dp"
            android:layout_gravity="start"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:background="@color/mm_white" />
    </android.support.v4.widget.DrawerLayout>
    

    经过几天的斗争,我意识到问题实际上是我传递了错误的FragmentManagerViewPager。应该使用的是Fragment.getChildFragmentManager()返回的FragmentManager。这是有道理的,因为是Fragment本身将子Fragment的状态存储在ViewPager中。我不知道为什么我以前不能做到这一点,但现在应用程序的生命周期与以下设置在我的主要FragmentonCreate()方法工作正常:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.mm_main_fragment, container, false);
        mMMActivity = (MMActivity) container.getContext();
        mMMActivity.getSupportActionBar().setTitle(getString(R.string.mm_toolbar_title_main_fragment));
        // Setup ViewPager.
        mViewPager = (ViewPager) view.findViewById(R.id.mm_pager);
        mAdapter = new MMPagerAdapter(getChildFragmentManager(), mMMActivity); // <-- This is the key
        mViewPager.setAdapter(mAdapter);
        mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
        mViewPager.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES);
        // Setup TabLayout.
        TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout);
        tabLayout.setupWithViewPager(mViewPager);
        return view;
    }
    

    这也是另一个问题的答案。作为旁注,我使用此解决方案在我的ViewPager中获得Fragment s,它在生命周期中工作得很好。

    测试应用程序:

    在这个问题的研究中,我为此创建了一个测试应用程序。感兴趣的人可以从这里克隆它。它的颜色很难看。

    就我所知,你的问题已经解决了。我过去也遇到过类似的问题。也许这有助于其他人尝试类似的东西。

    帮助我保持片段状态的方法是替换

    @Override
    public Fragment getItem(int position)
    {
        switch (position)
        {
            case PagerConstants.PAGE_FILTER_RECIPES:
                return new FilteredRecipesFragment();
            case PagerConstants.PAGE_SELECTED_RECIPES:
                return new SelectedRecipesFragment();
            case PagerConstants.PAGE_SHOPPING_LIST:
                return new ShoppingListFragment();
            default:
                return null;
        }
    }
    

    方法。

    我改变了Adapter,以保持FragmentHashMap。通过这种方式,片段只创建一次,然后从HashMap中检索。根据您的结构,您可以使用ArrayList代替HashMap。

    这是以一种高效和"智能"的方式实现getItemPosition(Object object)return POSITION_NONE;或项目的适当位置。

    我真的不记得我是否在适配器中有getChildFragmentManager

    如果你需要更多的信息,我想我可以深入研究我的文件系统:)

    最新更新