web-dev-qa-db-fra.com

ViewPager et fragments - quelle est la bonne façon de stocker l'état de fragment?

Les fragments semblent très pratiques pour séparer la logique de l'interface utilisateur en quelques modules. Mais avec ViewPager son cycle de vie est encore embué pour moi. Les pensées du gourou sont donc indispensables!

Modifier

Voir la solution idiote ci-dessous ;-)

Portée

L'activité principale a un ViewPager avec des fragments. Ces fragments pourraient implémenter une logique légèrement différente pour d'autres activités (sous-principales), de sorte que les données des fragments sont remplies via une interface de rappel dans l'activité. Et tout fonctionne bien lors du premier lancement, mais! ...

Problème

Lorsque l'activité est recréée (par exemple lors d'un changement d'orientation), les fragments ViewPager le sont également. Le code (vous trouverez ci-dessous) indique que chaque fois que l'activité est créée, j'essaie de créer un nouvel adaptateur de fragments ViewPager identique à celui utilisé pour les fragments (mais c'est peut-être le problème), mais FragmentManager a déjà tous ces fragments stockés quelque part. (où?) et lance le mécanisme de récréation pour ceux-ci. Le mécanisme de recréation appelle donc le "vieux" fragment onAttach, onCreateView, etc. avec mon appel d'interface de rappel pour le lancement de données via la méthode implémentée de l'Activité. Mais cette méthode pointe vers le fragment nouvellement créé qui est créé via la méthode onCreate de l'activité.

Problème

J'utilise peut-être de mauvais modèles, mais même Android 3 Pro Book n'a pas grand chose à ce sujet. Donc, s'il vous plaît, donnez-moi un coup-de-poing et expliquez comment le faire de la bonne façon. Merci beaucoup!

Code

Activité principale

public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {

private MessagesFragment mMessagesFragment;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    setContentView(R.layout.viewpager_container);
    new DefaultToolbar(this);

    // create fragments to use
    mMessagesFragment = new MessagesFragment();
    mStreamsFragment = new StreamsFragment();

    // set titles and fragments for view pager
    Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
    screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
    screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);

    // instantiate view pager via adapter
    mPager = (ViewPager) findViewById(R.id.viewpager_pager);
    mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);

    // set title indicator
    TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
    indicator.setViewPager(mPager, 1);

}

/* set of fragments callback interface implementations */

@Override
public void onMessageInitialisation() {

    Logger.d("Dash onMessageInitialisation");
    if (mMessagesFragment != null)
        mMessagesFragment.loadLastMessages();
}

@Override
public void onMessageSelected(Message selectedMessage) {

    Intent intent = new Intent(this, StreamActivity.class);
    intent.putExtra(Message.class.getName(), selectedMessage);
    startActivity(intent);
}

BasePagerActivity aka helper

public class BasePagerActivity extends FragmentActivity {

BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}

Adaptateur

public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {

private Map<String, Fragment> mScreens;

public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {

    super(fm);
    this.mScreens = screenMap;
}

@Override
public Fragment getItem(int position) {

    return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}

@Override
public int getCount() {

    return mScreens.size();
}

@Override
public String getTitle(int position) {

    return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}

// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {

    // TODO Auto-generated method stub
}

}

Fragment

public class MessagesFragment extends ListFragment {

private boolean mIsLastMessages;

private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;

private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;

// define callback interface
public interface OnMessageListActionListener {
    public void onMessageInitialisation();
    public void onMessageSelected(Message selectedMessage);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // setting callback
    mListener = (OnMessageListActionListener) activity;
    mIsLastMessages = activity instanceof DashboardActivity;

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    inflater.inflate(R.layout.fragment_listview, container);
    mProgressView = inflater.inflate(R.layout.listrow_progress, null);
    mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // instantiate loading task
    mLoadMessagesTask = new LoadMessagesTask();

    // instantiate list of messages
    mMessagesList = new ArrayList<Message>();
    mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
    setListAdapter(mAdapter);
}

@Override
public void onResume() {
    mListener.onMessageInitialisation();
    super.onResume();
}

public void onListItemClick(ListView l, View v, int position, long id) {
    Message selectedMessage = (Message) getListAdapter().getItem(position);
    mListener.onMessageSelected(selectedMessage);
    super.onListItemClick(l, v, position, id);
}

/* public methods to load messages from Host acitivity, etc... */
}

Solution

La solution idiote consiste à enregistrer les fragments dans onSaveInstanceState (de Host Activity) avec putFragment et à les insérer à onCreate via getFragment. Mais j'ai toujours le sentiment étrange que les choses ne devraient pas fonctionner comme ça ... Voir le code ci-dessous:

    @Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    getSupportFragmentManager()
            .putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}

protected void onCreate(Bundle savedInstanceState) {
    Logger.d("Dash onCreate");
    super.onCreate(savedInstanceState);

    ...
    // create fragments to use
    if (savedInstanceState != null) {
        mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
                savedInstanceState, MessagesFragment.class.getName());
                StreamsFragment.class.getName());
    }
    if (mMessagesFragment == null)
        mMessagesFragment = new MessagesFragment();
    ...
}
478
Aleksey Malevaniy

Lorsque FragmentPagerAdapter ajoute un fragment au FragmentManager, il utilise une balise spéciale en fonction de la position particulière à laquelle le fragment sera placé. FragmentPagerAdapter.getItem(int position) n'est appelé que lorsqu'un fragment pour cette position n'existe pas. Après la rotation, Android remarquera qu'il a déjà créé/enregistré un fragment pour cette position particulière et essaie donc simplement de se reconnecter avec celui-ci avec FragmentManager.findFragmentByTag(), au lieu de créer un nouveau. Tout cela est gratuit lorsque vous utilisez la variable FragmentPagerAdapter et c’est pourquoi il est habituel d’avoir votre code d’initialisation de fragment dans la méthode getItem(int).

Même si nous n'utilisions pas FragmentPagerAdapter, il n'est pas conseillé de créer un nouveau fragment à chaque fois dans Activity.onCreate(Bundle). Comme vous l'avez remarqué, lorsqu'un fragment est ajouté à FragmentManager, il est recréé pour vous après la rotation et il n'est pas nécessaire de l'ajouter à nouveau. Cela est une cause fréquente d’erreurs lorsqu’on travaille avec des fragments.

Une approche habituelle lorsque vous travaillez avec des fragments est la suivante:

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

    ...

    CustomFragment fragment;
    if (savedInstanceState != null) {
        fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
    } else {
        fragment = new CustomFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit(); 
    }

    ...

}

Lorsque vous utilisez un FragmentPagerAdapter, nous abandonnons la gestion des fragments à l'adaptateur et nous ne devons pas exécuter les étapes ci-dessus. Par défaut, il ne préchargera qu'un fragment devant et derrière la position actuelle (même s'il ne les détruit pas sauf si vous utilisez FragmentStatePagerAdapter). Ceci est contrôlé par ViewPager.setOffscreenPageLimit (int) . De ce fait, l'appel des méthodes directement sur les fragments extérieurs à l'adaptateur n'est pas garanti, car elles risquent même de ne pas être en vie.

Pour résumer, votre solution d’utiliser putFragment pour pouvoir ensuite obtenir une référence n’est pas si folle, ni si différente de la façon habituelle d’utiliser des fragments (ci-dessus). Sinon, il est difficile d'obtenir une référence car le fragment est ajouté par l'adaptateur, et pas vous personnellement. Assurez-vous simplement que la offscreenPageLimit est suffisamment élevée pour charger les fragments souhaités à tout moment, car vous comptez sur sa présence. Cela contourne les capacités de chargement paresseux de ViewPager, mais semble être ce que vous désirez pour votre application.

Une autre approche consiste à remplacer FragmentPageAdapter.instantiateItem(View, int) et à enregistrer une référence au fragment renvoyé par le super appel avant de le renvoyer (il a la logique pour rechercher le fragment, s'il est déjà présent).

Pour une image plus complète, jetez un coup d’œil à la source de FragmentPagerAdapter (court) et ViewPager (long).

443
antonyt

Je veux offrir une solution qui étend les antonyt réponse merveilleuse et mention de la substitution de FragmentPageAdapter.instantiateItem(View, int) pour enregistrer les références à créé Fragments afin que vous peut travailler sur eux plus tard. Cela devrait également fonctionner avec FragmentStatePagerAdapter; voir les notes pour plus de détails.


Voici un exemple simple sur la façon d'obtenir une référence à Fragments renvoyée par FragmentPagerAdapter qui ne repose pas sur l'ensemble tags interne défini sur Fragments. La clé consiste à remplacer instantiateItem() et à enregistrer les références à cet emplacement à la place de dans getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

        public CustomPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

ou si vous préférez utiliser tags au lieu des variables/références de membre de classe à Fragments, vous pouvez également saisir le tags défini par FragmentPagerAdapter de la même manière: REMARQUE: cela ne s'applique pas à FragmentStatePagerAdapter car il ne définit pas tags lors de la création de son Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

Notez que cette méthode ne repose PAS sur la réplication de la variable tag interne définie par FragmentPagerAdapter et utilise à la place les API appropriées pour les récupérer. De cette manière, même si le tag change dans les versions futures du SupportLibrary, vous serez toujours en sécurité.


N'oubliez pas que, selon la conception de votre Activity, le Fragments sur lequel vous essayez de travailler peut exister ou ne pas exister, vous avez donc rendre compte de cela en faisant les vérifications null avant d’utiliser vos références.

De plus, si au lieu de cela vous travaillez avec FragmentStatePagerAdapter, vous ne voulez pas conserver de références dures à votre Fragments car vous pourriez en avoir beaucoup et les références dures les garderaient inutilement en mémoire. Enregistrez plutôt les références Fragment dans les variables WeakReference au lieu de celles standard. Comme ça:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}
35
Tony Chan

J'ai trouvé une autre solution relativement facile à votre question.

Comme vous pouvez le voir à partir du code source de FragmentPagerAdapter , les fragments gérés par FragmentPagerAdapter sont stockés dans le FragmentManager sous la balise générée à l'aide de:

String tag="Android:switcher:" + viewId + ":" + index;

La viewId est la container.getId(), la container est votre instance ViewPager. La index est la position du fragment. Par conséquent, vous pouvez enregistrer l'ID d'objet dans le outState:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("viewpagerid" , mViewPager.getId() );
}

@Override
    protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null)
        viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );  

    MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);        
    mViewPager = (ViewPager) findViewById(R.id.pager);
    if (viewpagerid != -1 ){
        mViewPager.setId(viewpagerid);
    }else{
        viewpagerid=mViewPager.getId();
    }
    mViewPager.setAdapter(titleAdapter);

Si vous souhaitez communiquer avec ce fragment, vous pouvez obtenir si de FragmentManager, par exemple:

getSupportFragmentManager().findFragmentByTag("Android:switcher:" + viewpagerid + ":0")
18
user2110066

Je souhaite proposer une solution alternative pour un cas peut-être légèrement différent, car bon nombre de mes recherches de réponses m'ont conduit à ce fil.

Mon cas - Je crée/ajoute des pages de manière dynamique et les glisse dans un ViewPager, mais lors de la rotation (onConfigurationChange), je me retrouve avec une nouvelle page car, bien entendu, OnCreate est appelé à nouveau. Mais je veux garder une référence à toutes les pages créées avant la rotation.

Problème - Je n'ai pas d'identifiant unique pour chaque fragment que je crée, aussi le seul moyen de faire référence était de stocker en quelque sorte les références dans un tableau à restaurer après le changement de rotation/configuration.

Solution de contournement - Le concept clé consistait à faire en sorte que l'activité (qui affiche les fragments) gère également le tableau de références aux fragments existants, car cette activité peut utiliser des ensembles dans onSaveInstanceState.

public class MainActivity extends FragmentActivity

Donc, dans cette activité, je déclare un membre privé pour suivre les pages ouvertes

private List<Fragment> retainedPages = new ArrayList<Fragment>();

Ceci est mis à jour chaque fois que onSaveInstanceState est appelé et restauré dans onCreate

@Override
protected void onSaveInstanceState(Bundle outState) {
    retainedPages = _adapter.exportList();
    outState.putSerializable("retainedPages", (Serializable) retainedPages);
    super.onSaveInstanceState(outState);
}

... donc, une fois stocké, il peut être récupéré ...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
    }
    _mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
    _adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
    if (retainedPages.size() > 0) {
        _adapter.importList(retainedPages);
    }
    _mViewPager.setAdapter(_adapter);
    _mViewPager.setCurrentItem(_adapter.getCount()-1);
}

C’était les modifications nécessaires à l’activité principale et j’avais donc besoin des membres et des méthodes de FragmentPagerAdapter pour que cela fonctionne.

public class ViewPagerAdapter extends FragmentPagerAdapter

une construction identique (comme indiqué ci-dessus dans MainActivity)

private List<Fragment> _pages = new ArrayList<Fragment>();

et cette synchronisation (telle qu'utilisée ci-dessus dans onSaveInstanceState) est spécifiquement supportée par les méthodes

public List<Fragment> exportList() {
    return _pages;
}

public void importList(List<Fragment> savedPages) {
    _pages = savedPages;
}

Et puis finalement, dans la classe fragment

public class CustomFragment extends Fragment

pour que tout cela fonctionne, il y avait deux changements, d'abord

public class CustomFragment extends Fragment implements Serializable

puis en ajoutant ceci à onCreate afin que les fragments ne soient pas détruits

setRetainInstance(true);

Je suis toujours en train de réfléchir aux fragments et au cycle de vie de Android. Par conséquent, il existe peut-être des redondances/des inefficacités dans cette méthode. Mais cela fonctionne pour moi et j'espère que cela pourrait être utile pour d'autres personnes ayant des cas similaires aux miens.

16
Frank Yin

Ma solution est très grossière mais fonctionne: étant mes fragments créés dynamiquement à partir de données conservées, je supprime simplement tous les fragments de la PageAdapter avant d'appeler super.onSaveInstanceState(), puis de les recréer à la création d'activité:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
    mSectionsPagerAdapter.removeAllfragments();
    super.onSaveInstanceState(outState);
}

Vous ne pouvez pas les supprimer dans onDestroy(), sinon vous obtenez cette exception:

Java.lang.IllegalStateException: Impossible d'effectuer cette action après onSaveInstanceState

Voici le code dans l'adaptateur de page:

public void removeAllfragments()
{
    if ( mFragmentList != null ) {
        for ( Fragment fragment : mFragmentList ) {
            mFm.beginTransaction().remove(fragment).commit();
        }
        mFragmentList.clear();
        notifyDataSetChanged();
    }
}

Je ne fais que sauvegarder la page actuelle et la restaurer dans onCreate(), après la création des fragments.

if (savedInstanceState != null)
    mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );  
8
Sir John

Qu'est-ce que c'est BasePagerAdapter? Vous devez utiliser l’un des adaptateurs de pageur standard, soit FragmentPagerAdapter, soit FragmentStatePagerAdapter, selon que vous souhaitez conserver les fragments dont le ViewPager n’a plus besoin et les conserver. ) ou ont leur état sauvegardé (ce dernier) et recréé si nécessaire.

Exemple de code pour utiliser ViewPager peut être trouvé ici

Il est vrai que la gestion des fragments dans un pager de vue sur plusieurs instances d’activité est un peu compliquée, car la FragmentManager du cadre prend en charge la sauvegarde de l’état et la restauration des fragments actifs créés par le pager. Tout ce que cela signifie réellement, c’est que lors de l’initialisation, l’adaptateur doit s’assurer qu’il se reconnecte avec tous les fragments restaurés existants. Vous pouvez regarder le code pour FragmentPagerAdapter ou FragmentStatePagerAdapter pour voir comment faire.

4
hackbod

Si quelqu'un rencontre des problèmes avec son FragmentStatePagerAdapter qui ne restaure pas correctement l'état de ses fragments ... c'est-à-dire ... de nouveaux fragments sont créés par FragmentStatePagerAdapter au lieu de les restaurer à partir de l'état ...

Assurez-vous d'appeler ViewPager.setOffscreenPageLimit() AVANT d'appeler ViewPager.setAdapeter(fragmentStatePagerAdapter)

Lorsqu’il appelle ViewPager.setOffscreenPageLimit()..., le ViewPager immédiatement regarde son adaptateur et tente de récupérer ses fragments. Cela peut se produire avant que ViewPager ait une chance de restaurer les fragments de savedInstanceState (créant ainsi de nouveaux fragments qui ne peuvent pas être réinitialisés à partir de SavedInstanceState car ils sont nouveaux).

2
dell116

Je suis venu avec cette solution simple et élégante. Cela suppose que l'activité est responsable de la création des fragments et que l'adaptateur ne les sert que.

C'est le code de l'adaptateur (rien de bizarre ici, à part le fait que mFragments est une liste de fragments maintenus par l'activité)

class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

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

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        TabFragment fragment = (TabFragment)mFragments.get(position);
        return fragment.getTitle();
    }
} 

Tout le problème de ce fil est d'obtenir une référence des "anciens" fragments, j'utilise donc ce code dans l'activité onCreate.

    if (savedInstanceState!=null) {
        if (getSupportFragmentManager().getFragments()!=null) {
            for (Fragment fragment : getSupportFragmentManager().getFragments()) {
                mFragments.add(fragment);
            }
        }
    }

Bien sûr, vous pouvez affiner ce code si nécessaire, par exemple en vous assurant que les fragments sont des instances d'une classe particulière.

0
Merlevede

Pour obtenir les fragments après le changement d'orientation, vous devez utiliser le .getTag ().

    getSupportFragmentManager().findFragmentByTag("Android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)

Pour un peu plus de maniement, j'ai écrit ma propre ArrayList pour que mon PageAdapter reçoive le fragment par viewPagerId et FragmentClass à n'importe quelle position:

public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";

private ArrayList<MyPageBuilder> fragmentPages;

public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
    super(fm);
    fragmentPages = fragments;
}

@Override
public Fragment getItem(int position) {
    return this.fragmentPages.get(position).getFragment();
}

@Override
public CharSequence getPageTitle(int position) {
    return this.fragmentPages.get(position).getPageTitle();
}

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


public int getItemPosition(Object object) {
    //benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden

    Log.d(logTAG, object.getClass().getName());
    return POSITION_NONE;
}

public Fragment getFragment(int position) {
    return getItem(position);
}

public String getTag(int position, int viewPagerId) {
    //getSupportFragmentManager().findFragmentByTag("Android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())

    return "Android:switcher:" + viewPagerId + ":" + position;
}

public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
    return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}


public static class MyPageBuilder {

    private Fragment fragment;

    public Fragment getFragment() {
        return fragment;
    }

    public void setFragment(Fragment fragment) {
        this.fragment = fragment;
    }

    private String pageTitle;

    public String getPageTitle() {
        return pageTitle;
    }

    public void setPageTitle(String pageTitle) {
        this.pageTitle = pageTitle;
    }

    private int icon;

    public int getIconUnselected() {
        return icon;
    }

    public void setIconUnselected(int iconUnselected) {
        this.icon = iconUnselected;
    }

    private int iconSelected;

    public int getIconSelected() {
        return iconSelected;
    }

    public void setIconSelected(int iconSelected) {
        this.iconSelected = iconSelected;
    }

    public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
        this.pageTitle = pageTitle;
        this.icon = icon;
        this.iconSelected = selectedIcon;
        this.fragment = frag;
    }
}

public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
    private final String logTAG = MyPageArrayList.class.getName() + ".";

    public MyPageBuilder get(Class cls) {
        // Fragment über FragmentClass holen
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return super.get(indexOf(item));
            }
        }
        return null;
    }

    public String getTag(int viewPagerId, Class cls) {
        // Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
        for (MyPageBuilder item : this) {
            if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
                return "Android:switcher:" + viewPagerId + ":" + indexOf(item);
            }
        }
        return null;
    }
}

Il suffit donc de créer une MyPageArrayList avec les fragments:

    myFragPages = new MyPageAdapter.MyPageArrayList();

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_data_frag),
            R.drawable.ic_sd_storage_24dp,
            R.drawable.ic_sd_storage_selected_24dp,
            new WidgetDataFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_color_frag),
            R.drawable.ic_color_24dp,
            R.drawable.ic_color_selected_24dp,
            new WidgetColorFrag()));

    myFragPages.add(new MyPageAdapter.MyPageBuilder(
            getString(R.string.widget_config_textsize_frag),
            R.drawable.ic_settings_widget_24dp,
            R.drawable.ic_settings_selected_24dp,
            new WidgetTextSizeFrag()));

et ajoutez-les au viewPager:

    mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
    myViewPager.setAdapter(mAdapter);

après cela, vous pouvez obtenir après l'orientation changer le bon fragment en utilisant sa classe:

        WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
            .findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));
0
Scrounger