web-dev-qa-db-fra.com

Enregistrement de l'état du fragment dans ViewPager

J'ai essayé l'exemple de code de l'API et cela n'a pas vraiment fonctionné alors j'ai implémenté le mien:

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>

Ma question est, au lieu de créer une nouvelle instance de Fragment chaque fois que l'utilisateur glisse vers la gauche et la droite, comment puis-je enregistrer l'état d'un fragment (dans une structure de données), puis le restaurer?

Démo de l'API ne semble pas du tout avoir de code d'enregistrement des informations d'état.

45
Android Noob

Utilisez ViewPager.setOffscreenPageLimit() dans FragmentActivity

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

Voir plus sur Comment dire à mon FragmentPagerAdapter personnalisé de cesser de détruire mes fragments?

118
Megamind

remplacez simplement cette méthode dans FragmentpagerAdapter

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

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

à partir de votre code

22
Jinu

pdate: Dans le getItem de MyAdapter vous retournez une nouvelle instance de votre Fragment. Android n'appellera pas getItem à chaque coup. Il appellera seulement getItem pour obtenir une nouvelle instance, mais réutilisera les instances existantes tant qu'elles sont disponibles.

À propos de la partie État de la démo. Je ne peux pas t'aider. Je pense que les techniques normales de restauration de l'état dans les fractions/activités s'appliquent ici, donc rien de spécial lors du chargement dans un ViewPager (mais je peux me tromper).

14
Jeroen

Si vous remplacez

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

sans appeler super, le fragment ne sera pas détruit et sera réutilisé.

13
Marco

L'adaptateur doit être étendu à partir de FragmentStatePagerAdapter au lieu de FragmentPageAdapter et l'adaptateur gardera la référence de la position à l'élément de fragment. Sur le parent d'activité ou de fragment, définissez l'écouteur OnPageChangeListener pour que PageIndicator détecte la position de l'élément de fragment a été activé et met à jour l'état des données associées.

À propos de l'état des données de l'élément de fragment, je pense que devrait enregistrer/restaurer l'état du parent Activity ou Fragement.

L'adaptateur peut ajouter du code comme suit:

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

J'espère que cette aide.

3
ThanhHH

J'ai trébuché sur un terrain similaire ... Je voulais garder mes fragments enregistrés dans mon adaptateur pour éviter un rechargement inutile car je n'avais que 5 fragments dans le FragmentPagerAdapter.

J'ai essentiellement dû créer un tableau et remplacer la méthode 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;
}
0
Kamal