web-dev-qa-db-fra.com

Android FragmentStatePagerAdapter

Je travaille avec un FragmentStatePagerAdapter en utilisant cet exemple .

La classe MyAdapter est implémentée comme suit:

public static class MyAdapter extends FragmentStatePagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {
            return ArrayListFragment.newInstance(position);
        }
    }

La classe ListFragment inclut la méthode suivante pour créer une nouvelle instance:

    /**
     * Create a new instance of CountingFragment, providing "num"
     * as an argument.
     */
    static ArrayListFragment newInstance(int num) {
        ArrayListFragment f = new ArrayListFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

Lorsque je crée un nouvel adaptateur de paginisateur d'état de fragment dans mon activité, getItem est appelé, qui à son tour appelle la méthode newInstance de la classe ListFragment. C’est génial quand je veux créer un nouveau fragment. 

Mais il m'est difficile de savoir comment modifier getItem (si nécessaire) pour obtenir l'objet fragment lorsqu'il existe déjà et les pages utilisateur, par exemple, de la page 2 à la page 1. J'aimerais que mon activité soit récupérer ce fragment existant précédemment créé afin qu'il puisse exécuter une classe interne AsyncMethod, qui réside dans la classe fragment. 

17
mraviator

J'ai mis en place quelque chose de similaire à ce que vous avez. J'ai étendu la classe FragmentPagerAdapter comme ceci:

public class ContactsFragmentPagerAdapter extends FragmentPagerAdapter {
    ActionBar mActionBar;
    private List<Fragment> mFragments;

    public ContactsFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        mFragments = fragments;
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    public void setActionBar(ActionBar bar) {
        mActionBar = bar;
    }
}

Notez que j'ai ajouté un argument au constructeur pour lui transmettre les objets List of Fragment. De cette façon, la méthode getItem() de cette classe peut renvoyer toute classe qui étend Fragment ou l’une de ses sous-classes et non une classe ArrayListFragment spécifique comme vous l’avez fait.

Dans la Activity où j'instancie ma sous-classe de FragmentPagerAdapter, j'ai passé dans la liste des objets Fragment:

Classe l'instanciation de FragmentPagerAdapter

public final class ContactManager extends Activity {
    private ContactsFragmentPagerAdapter mAdapter;
    private ViewPager mPager;
    public ActionBar mActionBar;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.contact_manager);

        List<Fragment> fragments = new Vector<Fragment>();
        fragments.add(Fragment.instantiate(this, ContactsListFragment.class.getName()));
        fragments.add(Fragment.instantiate(this, GroupsListFragment.class.getName()));
        mAdapter = new ContactsFragmentPagerAdapter(this.getFragmentManager(), fragments);

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

        mPager.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int arg0) {}

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {}

            @Override
            public void onPageSelected(int arg0) {
                mActionBar.getTabAt(arg0).select();
            }
        });

    }

}

En accédant à la variable "fragments", vous pouvez accéder à une Fragment créée précédemment afin de pouvoir exécuter des méthodes de cette Fragment.

3
toobsco42

Je pense que la solution est beaucoup plus simple que les réponses fournies.

Si vous consultez la source de FragmentStatePagerAdapter.instantiateItem() , vous remarquerez que instantiateItem() gère cette logique pour vous et que votre implémentation de getItem() devrait toujours renvoyer une nouvelle instance.

Donc, pour renvoyer un fragment existant, faites simplement (en supposant que vous appelez de ViewPager):

Fragment f = (Fragment) getAdapter().instantiateItem(this, getCurrentItem());
40
imiric

Bien que la solution de @ imiric soit très concise, cela me dérange toujours de savoir qu'elle nécessite une connaissance de la mise en œuvre de la classe. Ma solution consiste à ajouter les éléments suivants à votre adaptateur, ce qui devrait se comporter correctement avec un FragmentStatePagerAdapter en détruisant éventuellement des fragments:

    private SparseArray<WeakReference<Fragment>> mFragments = new SparseArray<>();

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment f = (Fragment) super.instantiateItem(container, position);
        mFragments.put(position, new WeakReference<>(f));  // Remember what fragment was in position
        return f;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        mFragments.remove(position);
    }

    public Fragment getFragment(int position) {
        WeakReference<Fragment> ref = mFragments.get(position);
        Fragment f = ref != null ? ref.get() : null;
        if (f == null) {
            Log.d(TAG, "fragment for " + position + " is null!");
        }
        return f;
    }
7
qix
public class MyPagerAdapter extends FragmentStatePagerAdapter {
    final Context m_context;
    final WeakReference<Fragment>[] m_fragments;

    public DetailPagerAdapter(FragmentManager fm) {
        super(fm);
        m_context = ...
                m_fragments = new WeakReference[enter size here];
    }

    @Override
    public Fragment getItem(int position) {
        final Fragment fragment = instantiate your fragment;
        m_fragments[position] = new WeakReference<Fragment>(fragment);
        return fragment;
    }

    @Override
    public int getCount() {
        return ...
    }

    public Fragment getFragment(final int position) {
        return m_fragments[position] == null ? null :
            m_fragments[position].get();
    }
}
5
farid_z

Vous n'avez pas besoin de modifier FragmentPagerAdapter, car les fragments sont mis en cache par FragmentManager. Donc, vous devez avoir besoin de trouver à l'intérieur. Utilisez cette fonction pour rechercher le fragment par position d’adaptateur de pageur.

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "Android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

Exemple de code pour l'API de support v4.

0
Daniel De León