Сохранение состояния фрагмента в ViewPager - PullRequest
43 голосов
/ 26 июля 2011

Я опробовал пример кода из 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;
    }

}

}

main.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 , похоже, вообще не имеет кода для сохранения информации о состоянии.

Ответы [ 6 ]

116 голосов
/ 11 ноября 2013

Использовать ViewPager.setOffscreenPageLimit() в FragmentActivity

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

Подробнее на Как мне сказать, чтобы мой пользовательский FragmentPagerAdapter прекратил уничтожать мои фрагменты?

18 голосов
/ 21 января 2015

просто переопределить этот метод в 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);

из вашего кода

14 голосов
/ 01 августа 2011

Обновление: В getItem MyAdapter вы возвращаете новый экземпляр вашего фрагмента. Android не будет вызывать getItem при каждом пролистывании. Он будет вызывать getItem только для получения нового экземпляра, но будет повторно использовать существующие экземпляры, пока они доступны.

О государственной части Демо. Я не могу помочь тебе. Я думаю, что здесь применяются обычные методы восстановления состояния в Fragements / Activity, поэтому ничего особенного при загрузке в ViewPager (но я могу ошибаться).

13 голосов
/ 27 октября 2011

Если вы переопределите

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

без вызова super, фрагмент не будет уничтожен и будет использован повторно.

3 голосов
/ 05 июня 2013

Адаптер должен быть расширен от FragmentStatePagerAdapter вместо FragmentPageAdapter, и адаптер будет сохранять ссылку с позиции на элемент фрагмента. На родительском действии или фрагменте установите прослушиватель OnPageChangeListener для PageIndicator, чтобы определить, активирована ли позиция фрагмента и обновить состояние связанных данных.

О состоянии данных элемента фрагмента, я думаю, следует сохранить / восстановить состояние из родителя Activity или Fragement.

Адаптер может добавить следующий код:

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);
    }
    ...
}

Надеюсь, что это поможет.

0 голосов
/ 25 декабря 2014

Я споткнулся о похожую землю ... Я хотел сохранить свои фрагменты в своем адаптере, чтобы избежать бесполезной перезагрузки, поскольку у меня было только 5 фрагментов в FragmentPagerAdapter.

Мне пришлось создать массив и переопределить метод 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;
}
...