142 votes

Mise à jour de données dans ListFragment dans le cadre de ViewPager

Je suis à l'aide de la v4 compatibilité ViewPager dans Android. Mon FragmentActivity a un tas de données qui doivent être affichés de différentes manières sur les différentes pages de mon ViewPager. Jusqu'à présent j'ai juste 3 instances de la même ListFragment, mais dans le futur, je vais avoir 3 instances de différents ListFragments. Le ViewPager est sur une verticale de l'écran du téléphone, les listes ne sont pas côte à côte.

Maintenant un bouton sur le ListFragment commence un distinct en pleine page de l'activité (via le FragmentActivty), qui retourne et FragentActivity modifie les données, il enregistre, puis tente de mettre à jour toutes les vues de son ViewPager. C'est ici que je suis coincé.

public class ProgressMainActivity extends FragmentActivity
{
    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    ...
        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager) findViewById(R.id.viewpager);
        mPager.setAdapter(mAdapter);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        ...
        updateFragments();
        ...
    }
    public void updateFragments()
    {
        //Attempt 1:
        //mAdapter.notifyDataSetChanged();
        //mPager.setAdapter(mAdapter);

        //Attempt 2:
        //HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]);
        //fragment.updateDisplay();
    }

    public static class MyAdapter extends FragmentPagerAdapter implements
         TitleProvider
    {
      int[] fragId = {0,0,0,0,0};
      public MyAdapter(FragmentManager fm)
      {
         super(fm);
      }
      @Override
      public String getTitle(int position){
         return titles[position];
      }
      @Override
      public int getCount(){
         return titles.length;
      }

      @Override
      public Fragment getItem(int position)
      {

         Fragment frag = HomeListFragment.newInstance(position);
         //Attempt 2:
         //fragId[position] = frag.getId();
         return frag;
      }

      @Override
      public int getItemPosition(Object object) {
         return POSITION_NONE; //To make notifyDataSetChanged() do something
     }
   }

    public class HomeListFragment extends ListFragment
    {
    ...
        public static HomeListFragment newInstance(int num)
        {
            HomeListFragment f = new HomeListFragment();
            ...
            return f;
        }
   ...

Maintenant, comme vous pouvez le voir, ma première tentative a été de notifyDataSetChanged sur l'ensemble de la FragmentPagerAdapter, et cela a montré à mettre à jour les données parfois, mais d'autres, j'ai eu une IllegalStateException: vous ne Pouvez pas effectuer cette action après onSaveInstanceState.

Ma deuxième tentative involed vous essayez d'appeler une fonction de mise à jour dans mon ListFragment, mais getId dans getItem retourné 0. Selon les docs que j'ai essayé par

l'acquisition d'une référence pour le Fragment de la FragmentManager, à l'aide de findFragmentById() ou findFragmentByTag()

mais je ne sais pas la marque ou de l'id de mes Fragments! J'ai un android:id="@+id/viewpager" pour ViewPager, et un android:id="@android:id/liste" pour ma ListView dans le ListFragment mise en page, mais je ne pense pas qu'ils sont utiles.

Alors, comment puis-je soit a) mise à jour de l'ensemble de la viewpager en toute sécurité dans un aller (idéalement retour de l'utilisateur à la page qu'il était avant) - il est ok pour l'utilisateur de voir le changement de vue. Ou, de préférence, b) l'appel d'une fonction dans chaque ListFragment pour mettre à jour la liste manuellement.

Toute aide serait grandement appréciés!

257voto

Pēteris Caune Points 13662

Barkside réponse travaille avec FragmentPagerAdapter mais ne fonctionne pas avec FragmentStatePagerAdapter, car il n'a pas mis de balises sur des fragments de passer à FragmentManager.

Avec FragmentStatePagerAdapter il semble que nous pouvons obtenir par le biais de son instantiateItem(ViewGroup container, int position) appel. Elle retourne la référence de fragment à la position position. Si FragmentStatePagerAdapter déjà fait référence à l'fragment en question, instantiateItem renvoie simplement référence à ce fragment, et de ne pas faire appel getItem() de l'instancier à nouveau.

Donc, supposons que, je suis actuellement à la recherche au fragment n ° 50, et souhaitez accéder à l'fragment n ° 49. Parce qu'elles sont proches, il ya une bonne chance que le #49 sera déjà instancié. Donc,

ViewPager pager = findViewById(R.id.viewpager);
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)

154voto

barkside Points 2023

OK, je crois que j'ai trouvé un moyen pour effectuer la demande b) dans ma question alors je vais partager pour les autres. La balise de fragments à l'intérieur d'un ViewPager est dans la forme "android:switcher:VIEWPAGER_ID:INDEX", où VIEWPAGER_ID est le R. id.viewpager en XML de mise en page, et l'INDICE de la position dans le viewpager. Donc, si la position est connue (par exemple 0), je peux effectuer dans updateFragments():

      HomeListFragment fragment = 
          (HomeListFragment) getSupportFragmentManager().findFragmentByTag(
                       "android:switcher:"+R.id.viewpager+":0");
      if(fragment != null)  // could be null if not instantiated yet
      {
         if(fragment.getView() != null) 
         {
            // no need to call if fragment's onDestroyView() 
            //has since been called.
            fragment.updateDisplay(); // do what updates are required
         }
      }

Je n'ai aucune idée si c'est un moyen valable de le faire, mais ça fera l'affaire jusqu'à ce que quelque chose de mieux est suggéré.

58voto

faylon Points 3497

Essayez d’enregistrer la balise chaque fois qu’un fragment est instancié.

35voto

Frank Points 1074

Si vous me demandez, la deuxième solution sur la page ci-dessous, en gardant la trace de tous les "actifs" fragment de pages, c'est mieux: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

La réponse de barkside est trop hacky pour moi.

vous gardez une trace de tous les "actifs" fragment de pages. Dans ce cas, vous conservez la trace du fragment pages dans la FragmentStatePagerAdapter, qui est utilisé par le ViewPager.

private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>();

public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferences.put(index, myFragment);
    return myFragment;
}

Pour éviter de conserver une référence à "inactif" fragment de pages, nous avons besoin de mettre en œuvre la FragmentStatePagerAdapter de destroyItem(...) la méthode:

public void destroyItem(View container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferences.remove(position);
}

... et quand vous en avez besoin pour accéder à la page actuellement visible, puis, vous composez le:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

... où le MyAdapter de getFragment(int) méthode ressemble à ceci:

public MyFragment getFragment(int key) {
    return mPageReferences.get(key);
}

"

13voto

Ryan R Points 1270

Bon, après test de la méthode par @barkside ci-dessus, je ne pouvais pas le faire fonctionner avec mon application. Puis je me suis souvenu que le IOSched2012 application utilise un viewpager ainsi, et c'est là que j'ai trouvé ma solution. Il n'utilise pas de fragment de cartes d'identité ou des Balises que ce ne sont pas stockées par viewpager d'une manière aisément accessible.

Voici les éléments importants à partir du IOSched apps HomeActivity. Accorder une attention particulière aux commentaires, que là se trouve la clef.:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    // Since the pager fragments don't have known tags or IDs, the only way to persist the
    // reference is to use putFragment/getFragment. Remember, we're not persisting the exact
    // Fragment instance. This mechanism simply gives us a way to persist access to the
    // 'current' fragment instance for the given fragment (which changes across orientation
    // changes).
    //
    // The outcome of all this is that the "Refresh" menu button refreshes the stream across
    // orientation changes.
    if (mSocialStreamFragment != null) {
        getSupportFragmentManager().putFragment(outState, "stream_fragment",
                mSocialStreamFragment);
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if (mSocialStreamFragment == null) {
        mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager()
                .getFragment(savedInstanceState, "stream_fragment");
    }
}

Et stocker les instances de vous Fragments dans la FragmentPagerAdapter comme:

    private class HomePagerAdapter extends FragmentPagerAdapter {
    public HomePagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return (mMyScheduleFragment = new MyScheduleFragment());

            case 1:
                return (mExploreFragment = new ExploreFragment());

            case 2:
                return (mSocialStreamFragment = new SocialStreamFragment());
        }
        return null;
    }

Aussi, n'oubliez pas de protéger votre Fragment appels:

    if (mSocialStreamFragment != null) {
        mSocialStreamFragment.refresh();
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X