【问题标题】:Saving Fragment state in ViewPager在 ViewPager 中保存 Fragment 状态
【发布时间】:2011-10-13 15:40:50
【问题描述】:

我尝试了the sample code from the API,但它并没有真正起作用,所以我实现了自己的:

FragmentPagerSupport

public class FragmentPagerSupport extends FragmentActivity {

static final int NUM_ITEMS = 10;
MyAdapter mAdapter;
ViewPager mPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mAdapter = new MyAdapter(getSupportFragmentManager());
    Log.i("Pager", "mAdapter = " + mAdapter.toString());

    mPager = (ViewPager)findViewById(R.id.pager);
    if (mPager == null)
        Log.i("Pager", "mPager = null");
    else 
        Log.i("Pager", "mPager = " + mPager.toString());

    Log.i("Pager", "Setting Pager Adapter");
    mPager.setAdapter(mAdapter);
}

public static class MyAdapter extends FragmentPagerAdapter {
    public MyAdapter(FragmentManager fm) {
        super(fm);
        Log.i("Pager", "MyAdapter constructor");
    }

    @Override
    public int getCount() {
        Log.i("Pager", "MyAdapter.getCount()");
        return NUM_ITEMS;            
    }

    @Override
    public Fragment getItem(int position) {
        Log.i("Pager", "MyAdapter.getItem()");

        return TestFragment.newInstance(position);
    }
}

public static class TestFragment extends Fragment {

    public static TestFragment newInstance(int position) {
        Log.i("Pager", "TestFragment.newInstance()");

        TestFragment fragment = new TestFragment();

        Bundle args = new Bundle();
        args.putInt("position", position);
        fragment.setArguments(args);

        return fragment;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        Log.i("Pager", "TestFragment.onCreateView()");

        LinearLayout layout = (LinearLayout)inflater.inflate(R.layout.fragment_item, null);
        int position = getArguments().getInt("position");

        TextView tv = (TextView)layout.findViewById(R.id.text);
        tv.setText("Fragment # " + position);
        tv.setTextColor(Color.WHITE);
        tv.setTextSize(30);

        return layout;
    }

}

}

ma​​in.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
</LinearLayout>

fragment_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  
    android:id="@+id/text"
    android:layout_width="match_parent" 
    android:layout_height="match_parent"
    android:gravity="center" 
    android:text="@string/hello"
    />
</LinearLayout>

我的问题是,不是每次用户左右滑动时都创建一个新的Fragment 实例,而是如何保存片段的状态(到某些数据结构)然后恢复它?

API demo 似乎根本没有任何状态信息保存代码。

【问题讨论】:

  • 您首先将Fragments 放在数据结构中,然后编写getItem 方法,以便它访问正确的片段。假设你的片段在ArrayList&lt;YourFragment&gt; fragList 中,你只有getItem 方法return fragList.get(position)。简单,无需通过拧destroyItem 来破坏ViewPager

标签: android android-fragments android-viewpager


【解决方案1】:

FragmentActivity 中使用ViewPager.setOffscreenPageLimit()

ViewPager pager = (ViewPager) findViewById(R.id.viewPager);
pager.setOffscreenPageLimit(2);

How do I tell my custom FragmentPagerAdapter to stop destroying my fragments?查看更多信息

【讨论】:

  • 非动态视图寻呼机的简单有效的解决方案,因为您将保持片段的状态而无需做任何其他事情。如果您不需要太多内存资源,建议使用。
  • @Pelanes non dynamic view pagers 是什么意思,谢谢! ?
  • ViewPagers 具有固定数量的页面,用户(或应用程序自动)在其中添加/删除页面。
  • 当然这不是修复,因为它无助于正确保存状态。它只是防止寻呼机破坏未使用的片段,有效地将问题推到以后。例如何时旋转设备或暂停/恢复应用程序。
  • 这不能回答问题。您只提供了一种解决方法。
【解决方案2】:

只需在 FragmentpagerAdapter 中重写此方法

@Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // TODO Auto-generated method stub
            super.destroyItem(ViewGroup container, int position, Object object);
        }

删除super.destroyItem(ViewGroup container, int position, Object object);

来自您的代码

【讨论】:

  • 谢谢@Jinu,它可以了。
  • 你救了我的命 :)
  • 好一个。从我这边 +1。
  • 哇。这太棒了!
【解决方案3】:

更新: 在 MyAdapter 的 getItem 中,您返回 Fragment 的新实例。 Android 不会在每次滑动时调用 getItem。它只会调用 getItem 来获取新实例,但只要现有实例可用,就会重用它们。

关于演示的状态部分。我帮不了你。我认为在 Fragments/Activity 中恢复状态的常规技术适用于此,因此在 ViewPager 中加载它时没有什么特别的(但我可能错了)。

【讨论】:

  • 这是正确答案。无需覆盖destroyItem
【解决方案4】:

如果你覆盖

public void destroyItem(View container, int position, Object object)

不调用super,fragment不会被销毁,会被复用。

【讨论】:

  • 简单说明:此方法应添加到适配器中(即扩展 FragmentPagerAdapter 的类)。
  • 这不是你应该解决问题的方式,而且是非常糟糕的设计。请不要这样做,而是花点时间了解问题的真正含义以及@jtietema 的答案中详细介绍的极其微不足道的解决方案。
  • @DavidCowden 所说的。这是一个非常糟糕的黑客攻击。
  • 该方法已被弃用。您必须使用:@Override public void destroyItem(ViewGroup container, int position, Object object) { }
  • 再次声明:不,您必须使用 destroyItem()
【解决方案5】:

适配器应该从 FragmentStatePagerAdapter 而不是 FragmentPageAdapter 扩展,并且适配器将保持从位置到片段项的引用。在activity或fragment parent上,为PageIndicator设置监听OnPageChangeListener,检测fragment item的位置是否被激活,并更新相关数据的状态。

关于片段项的数据状态,我认为应该从 Activity 或 Fragment 父级保存/恢复状态。

适配器可以添加如下代码:

public static class MyAdapter extends FragmentStatePagerAdapter {
    private SparseArray<TestFragment> mPageReferenceMap 
                              = new SparseArray<TestFragment>();
    ...

    @Override 
    public Object instantiateItem(ViewGroup viewGroup, int position) {
        Object obj = super.instantiateItem(viewGroup, position);

        //Add the reference when fragment has been create or restore
        if (obj instanceof TestFragment) {
                TestFragment f= (TestFragment)obj;
                mPageReferenceMap.put(position, f);
        }

        return obj;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //Remove the reference when destroy it
        mPageReferenceMap.remove(position);

        super.destroyItem(container, position, object);
    }

    public TestFragment getFragment(int key) {

        return mPageReferenceMap.get(key);
    }
    ...
}

希望对您有所帮助。

【讨论】:

    【解决方案6】:

    我在类似的地方绊倒了... 我想将我的片段保存在我的适配器中以避免无用的重新加载,因为我在 FragmentPagerAdapter 中只有 5 个片段。

    我基本上必须创建一个数组并覆盖getItem(position) 方法:

    private StoryListFragment[] fragmentsArray
            = new StoryListFragment[getCount()];
    
    public SectionsAdapter(FragmentManager fm) {
        super(fm);
    }
    
    
    @Override
    public Fragment getItem(int position) {
    
        if(fragmentsArray[position]!=null)
            return fragmentsArray[position];
        StoryListFragment storyListFragment = null;
        switch(position){
            case(0):
                storyListFragment = StoryListFragment.newInstance(StoriesBank.NEWS);
                break;
            case(1):
                storyListFragment = StoryListFragment.newInstance(StoriesBank.FEATURES);
                break;
            case(2):
                 storyListFragment=  StoryListFragment.newInstance(StoriesBank.ARTS);
                break;
        }
    
        fragmentsArray[position] =  storyListFragment;
        return storyListFragment;
    }
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        StoryListFragment fragment = (StoryListFragment) super.instantiateItem(container, position);
        fragmentsArray[position] = fragment;
        return fragment;
    }
    

    【讨论】:

    • 但这意味着你拥有所有的片段,而且我也不确定在恢复活动和片段时这将如何工作(如果应用程序在后台并且操作系统决定会发生这种情况)它需要一些内存,所以它会关闭一些活动。
    • 是的。正如我所说,我使用这个系统是因为我只有 5 个轻量级片段,并且我需要随时跟踪它们。另外,如果开发者担心fragment删除,那么他/她可以在activity的onCreate()中做一个条件语句来重新实例化当前访问的fragment(或者全部,取决于具体情况) )。同样,这绝不是重量级片段或许多片段的活动的理想选择。
    • 这也是我做的,但我希望它不会造成问题,因为我让自己陷入了一个更复杂的情况:stackoverflow.com/q/27648565/878126
    猜你喜欢
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多