Remplacer le fragment à l'intérieur d'un ViewPager

268

J'essaie d'utiliser Fragment avec un ViewPagerutilisant le FragmentPagerAdapter. Ce que je cherche à réaliser, c'est de remplacer un fragment, positionné sur la première page du ViewPager, par un autre.

Le téléavertisseur est composé de deux pages. Le premier est le FirstPagerFragment, le second est le SecondPagerFragment. Cliquer sur un bouton de la première page. Je voudrais remplacer le FirstPagerFragmentpar le NextFragment.

Il y a mon code ci-dessous.

public class FragmentPagerActivity extends FragmentActivity {

    static final int NUM_ITEMS = 2;

    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new MyAdapter(getSupportFragmentManager());

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

    }


    /**
     * Pager Adapter
     */
    public static class MyAdapter extends FragmentPagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

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

        @Override
        public Fragment getItem(int position) {

            if(position == 0) {
                return FirstPageFragment.newInstance();
            } else {
                return SecondPageFragment.newInstance();
            }

        }
    }


    /**
     * Second Page FRAGMENT
     */
    public static class SecondPageFragment extends Fragment {

        public static SecondPageFragment newInstance() {
            SecondPageFragment f = new SecondPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.second, container, false);

        }
    }

    /**
     * FIRST PAGE FRAGMENT
     */
    public static class FirstPageFragment extends Fragment {

        Button button;

        public static FirstPageFragment newInstance() {
            FirstPageFragment f = new FirstPageFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            View root = inflater.inflate(R.layout.first, container, false);
            button = (Button) root.findViewById(R.id.button);
            button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    FragmentTransaction trans = getFragmentManager().beginTransaction();
                                    trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
                    trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
                    trans.addToBackStack(null);
                    trans.commit();

                }

            });

            return root;
        }

        /**
     * Next Page FRAGMENT in the First Page
     */
    public static class NextFragment extends Fragment {

        public static NextFragment newInstance() {
            NextFragment f = new NextFragment();
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            //Log.d("DEBUG", "onCreateView");
            return inflater.inflate(R.layout.next, container, false);

        }
    }
}

... et ici les fichiers xml

fragment_pager.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:padding="4dip"
        android:gravity="center_horizontal"
        android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">
    </android.support.v4.view.ViewPager>

</LinearLayout>

first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/first_fragment_root_id"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <Button android:id="@+id/button"
     android:layout_width="wrap_content" android:layout_height="wrap_content"
     android:text="to next"/>

</LinearLayout>

Maintenant, le problème ... quel ID dois-je utiliser dans

trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());

?

Si j'utilise R.id.first_fragment_root_id, le remplacement fonctionne, mais Hierarchy Viewer montre un comportement étrange, comme ci-dessous.

Au début, la situation est

après le remplacement, la situation est

Comme vous pouvez le voir, il y a quelque chose qui ne va pas, je m'attends à trouver le même état que celui indiqué dans la première image après avoir remplacé le fragment.

Nouilles
la source
Avez-vous en quelque sorte généré ce schéma de disposition ou l'avez-vous fait manuellement?
Jeroen Vannevel
@Noodles: Je dois faire de même. Votre question a généré un long fil avec de nombreuses réponses différentes. Qu'est-ce qui a le mieux fonctionné à la fin? Merci beaucoup!
narb
1
@JeroenVannevel vous l'avez peut-être déjà compris, mais ce schéma de disposition est developer.android.com/tools/performance/hierarchy-viewer/…
Ankit Popli
obtenu un problème besoin de votre aide stackoverflow.com/questions/40149039/…
albert

Réponses:

162

Il existe une autre solution qui n'a pas besoin de modifier le code source de ViewPageret FragmentStatePagerAdapter, et elle fonctionne avec la FragmentPagerAdapterclasse de base utilisée par l'auteur.

J'aimerais commencer par répondre à la question de l'auteur sur la pièce d'identité à utiliser; c'est l'ID du conteneur, c'est-à-dire l'ID du téléavertisseur lui-même. Cependant, comme vous l'avez probablement remarqué, l'utilisation de cet ID dans votre code ne provoque rien. Je vais vous expliquer pourquoi:

Tout d'abord, pour faire ViewPagerrepeupler les pages, vous devez appeler notifyDataSetChanged()qui réside dans la classe de base de votre adaptateur.

Deuxièmement, ViewPagerutilise la getItemPosition()méthode abstraite pour vérifier quelles pages doivent être détruites et lesquelles doivent être conservées. L'implémentation par défaut de cette fonction revient toujours POSITION_UNCHANGED, ce qui entraîne la ViewPagerconservation de toutes les pages actuelles et, par conséquent, la non-association de votre nouvelle page. Ainsi, pour que le remplacement de fragment fonctionne, il getItemPosition()doit être remplacé dans votre adaptateur et doit revenir POSITION_NONElorsqu'il est appelé avec un ancien fragment à masquer comme argument.

Cela signifie également que votre adaptateur doit toujours savoir quel fragment doit être affiché en position 0 FirstPageFragmentou NextFragment. Une façon de procéder consiste à fournir un écouteur lors de la création FirstPageFragment, qui sera appelé lorsqu'il sera temps de changer de fragment. Je pense que c'est une bonne chose cependant, de laisser votre adaptateur de fragment gérer tous les commutateurs de fragment et les appels à ViewPageret FragmentManager.

Troisièmement, FragmentPagerAdaptermet en cache les fragments utilisés par un nom dérivé de la position, donc s'il y avait un fragment à la position 0, il ne sera pas remplacé même si la classe est nouvelle. Il existe deux solutions, mais la plus simple consiste à utiliser la remove()fonction de FragmentTransaction, qui supprimera également sa balise.

C'était beaucoup de texte, voici du code qui devrait fonctionner dans votre cas:

public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {
            if (mFragmentAtPos0 == null)
            {
                mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
                {
                    public void onSwitchToNextFragment()
                    {
                        mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
                        mFragmentAtPos0 = NextFragment.newInstance();
                        notifyDataSetChanged();
                    }
                });
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

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

    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }
}

public interface FirstPageFragmentListener
{
    void onSwitchToNextFragment();
}

J'espère que cela aide n'importe qui!

wize
la source
24
fonctionne aussi pour moi, mais comment implémenter le bouton de retour pour afficher le premier fragment?
user1159819
6
Excellente explication, mais pourriez-vous publier le code source complet de cela, en particulier les classes FirstPageFragment et SecondPageFragment.
georgiecasey
6
Comment ajoutez-vous le FirstPageFragmentListener au bundle dans newInstance ()?
miguel
4
Pouvez-vous ajouter le code où vous gérez avec le FirstPageFragment.newInstance()paramètre d'écoute?
MiguelHincapieC
6
Cela ne fonctionne pour moi que si j'appelle commitNow () au lieu de commit () lors de la suppression du fragment. Je ne sais pas pourquoi mon cas d'utilisation est différent, mais j'espère que cela fera gagner du temps à quelqu'un.
Ian Lovejoy,
37

Depuis le 13 novembre 2012, le remplacement de fragments dans un ViewPager semble être devenu beaucoup plus facile. Google a publié Android 4.2 avec la prise en charge des fragments imbriqués, et il est également pris en charge dans la nouvelle bibliothèque de support Android v11, donc cela fonctionnera jusqu'à 1.6

C'est très similaire à la façon normale de remplacer un fragment, sauf que vous utilisez getChildFragmentManager. Il semble fonctionner sauf que le backstack de fragments imbriqués n'est pas sauté lorsque l'utilisateur clique sur le bouton de retour. Selon la solution de cette question liée, vous devez appeler manuellement le popBackStackImmediate () sur le gestionnaire enfant du fragment. Vous devez donc remplacer onBackPressed () de l'activité ViewPager où vous obtiendrez le fragment actuel de ViewPager et appelez getChildFragmentManager (). PopBackStackImmediate () dessus.

Obtenir le fragment actuellement affiché est également un peu hacky, j'ai utilisé cette sale solution "android: switcher: VIEWPAGER_ID: INDEX" mais vous pouvez également suivre vous-même tous les fragments du ViewPager comme expliqué dans la deuxième solution sur cette page .

Voici donc mon code pour un ViewPager avec 4 ListViews avec une vue détaillée affichée dans le ViewPager lorsque l'utilisateur clique sur une ligne, et avec le bouton de retour en marche. J'ai essayé d'inclure uniquement le code pertinent par souci de concision, alors laissez un commentaire si vous voulez que l'application complète soit téléchargée sur GitHub.

HomeActivity.java

 public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mAdapter = new FragmentAdapter(getSupportFragmentManager());
    mPager = (ViewPager)findViewById(R.id.pager);
    mPager.setAdapter(mAdapter);
    mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
    mIndicator.setViewPager(mPager);
}

// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
    Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
    if (fragment != null) // could be null if not instantiated yet
    {
        if (fragment.getView() != null) {
            // Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
            if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
                finish();
            }
        }
    }
}

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

    @Override
    public Fragment getItem(int position) {
        switch (position) {
        case 0:
            return ListProductsFragment.newInstance();
        case 1:
            return ListActiveSubstancesFragment.newInstance();
        case 2:
            return ListProductFunctionsFragment.newInstance();
        case 3:
            return ListCropsFragment.newInstance();
        default:
            return null;
        }
    }

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

 }
}

ListProductsFragment.java

public class ListProductsFragment extends SherlockFragment {
private ListView list;

public static ListProductsFragment newInstance() {
    ListProductsFragment f = new ListProductsFragment();
    return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View V = inflater.inflate(R.layout.list, container, false);
    list = (ListView)V.findViewById(android.R.id.list);
    list.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
          // This is important bit
          Fragment productDetailFragment = FragmentProductDetail.newInstance();
          FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
          transaction.addToBackStack(null);
          transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
        }
      });       
    return V;
}
}
georgiecasey
la source
7
Serez-vous possible de télécharger votre code sur github? Merci.
Gautham
4
@georgiecasey: J'ai téléchargé l'API 17, Android 4.2 qui m'a permis de commencer à utiliser getChildFragmentManager (). Mais je dois manquer quelque chose de votre solution, car je reçois maintenant des fragments anciens + nouveaux qui se chevauchent à l'écran. Remarque: J'essaie d'utiliser votre solution de concert avec l'exemple de code Google TabsAdapter de developer.android.com/reference/android/support/v4/view/… . Merci pour toutes suggestions et / ou code de référence.
gcl1
27
À quoi se réfère R.id.products_list_linear? Veuillez lier à votre code tel qu'il a été proposé.
howettl
1
@howettl les références d'ID au membre du fragment externe. La clé pour résoudre ce problème consiste à utiliser des fragments imbriqués (de cette façon, vous n'avez pas besoin de manipuler l'adaptateur Viewpager).
Indrek Kõue
2
Est-ce que cela est déjà arrivé à Github?
Baruch
32

Sur la base de la réponse de @wize, que j'ai trouvée utile et élégante, j'ai pu réaliser ce que je voulais en partie, car je voulais que la capacité revienne au premier fragment une fois remplacé. Je l'ai réalisé en modifiant un peu son code.

Ce serait le FragmentPagerAdapter:

public static class MyAdapter extends FragmentPagerAdapter {
    private final class CalendarPageListener implements
            CalendarPageFragmentListener {
        public void onSwitchToNextFragment() {
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                    .commit();
            if (mFragmentAtPos0 instanceof FirstFragment){
                mFragmentAtPos0 = NextFragment.newInstance(listener);
            }else{ // Instance of NextFragment
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            notifyDataSetChanged();
        }
    }

    CalendarPageListener listener = new CalendarPageListener();;
    private Fragment mFragmentAtPos0;
    private FragmentManager mFragmentManager;

    public MyAdapter(FragmentManager fm) {
        super(fm);
        mFragmentManager = fm;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
        if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }

    @Override
    public Fragment getItem(int position) {
        if (position == 0)
            return Portada.newInstance();
        if (position == 1) { // Position where you want to replace fragments
            if (mFragmentAtPos0 == null) {
                mFragmentAtPos0 = FirstFragment.newInstance(listener);
            }
            return mFragmentAtPos0;
        }
        if (position == 2)
            return Clasificacion.newInstance();
        if (position == 3)
            return Informacion.newInstance();

        return null;
    }
}

public interface CalendarPageFragmentListener {
    void onSwitchToNextFragment();
}

Pour effectuer le remplacement, il suffit de définir un champ statique, du type CalendarPageFragmentListeneret initialisé par les newInstanceméthodes des fragments correspondants et d'appeler FirstFragment.pageListener.onSwitchToNextFragment()ou NextFragment.pageListener.onSwitchToNextFragment()respictevely.

mdelolmo
la source
Comment effectuez-vous le remplacement? il n'est pas possible d'initialiser l'écouteur sans remplacer la méthode onSwitchToNext.
Leandros
1
ne perdez-vous pas l'état enregistré dans mFragmentAtPos0 si vous faites pivoter l'appareil?
seb
@seb, j'ai en fait amélioré cette solution afin de sauvegarder la mFragmentAtPos0référence lors de l'enregistrement de l'état d'activité. Ce n'est pas la solution la plus élégante, mais ça marche.
mdelolmo
Vous pouvez voir ma réponse . Il remplace les fragments et revient au premier.
Lyd
@mdelolmo Pourriez-vous jeter un oeil à cette question sur ViewPager? Merci stackoverflow.com/questions/27937250/…
Hoa Vu
21

J'ai implémenté une solution pour:

  • Remplacement de fragment dynamique à l'intérieur de l'onglet
  • Maintenance de l'historique par onglet
  • Travailler avec des changements d'orientation

Les astuces pour y parvenir sont les suivantes:

  • Utilisez la méthode notifyDataSetChanged () pour appliquer le remplacement de fragment
  • Utilisez le gestionnaire de fragments uniquement pour la phase arrière et non pour le remplacement des fragments
  • Maintenir l'historique en utilisant le modèle de souvenir (pile)

Le code de l'adaptateur est le suivant:

public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {

/** The sherlock fragment activity. */
private final SherlockFragmentActivity mActivity;

/** The action bar. */
private final ActionBar mActionBar;

/** The pager. */
private final ViewPager mPager;

/** The tabs. */
private List<TabInfo> mTabs = new LinkedList<TabInfo>();

/** The total number of tabs. */
private int TOTAL_TABS;

private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();

/**
 * Creates a new instance.
 *
 * @param activity the activity
 * @param pager    the pager
 */
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
    super(activity.getSupportFragmentManager());
    activity.getSupportFragmentManager();
    this.mActivity = activity;
    this.mActionBar = activity.getSupportActionBar();
    this.mPager = pager;
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}

/**
 * Adds the tab.
 *
 * @param image         the image
 * @param fragmentClass the class
 * @param args          the arguments
 */
public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
    final TabInfo tabInfo = new TabInfo(fragmentClass, args);
    final ActionBar.Tab tab = mActionBar.newTab();
    tab.setTabListener(this);
    tab.setTag(tabInfo);
    tab.setIcon(image);

    mTabs.add(tabInfo);
    mActionBar.addTab(tab);

    notifyDataSetChanged();
}

@Override
public Fragment getItem(final int position) {
    final TabInfo tabInfo = mTabs.get(position);
    return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
}

@Override
public int getItemPosition(final Object object) {
    /* Get the current position. */
    int position = mActionBar.getSelectedTab().getPosition();

    /* The default value. */
    int pos = POSITION_NONE;
    if (history.get(position).isEmpty()) {
        return POSITION_NONE;
    }

    /* Checks if the object exists in current history. */
    for (Stack<TabInfo> stack : history.values()) {
        TabInfo c = stack.peek();
        if (c.fragmentClass.getName().equals(object.getClass().getName())) {
            pos = POSITION_UNCHANGED;
            break;
        }
    }
    return pos;
}

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

@Override
public void onPageScrollStateChanged(int arg0) {
}

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

@Override
public void onPageSelected(int position) {
    mActionBar.setSelectedNavigationItem(position);
}

@Override
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
    TabInfo tabInfo = (TabInfo) tab.getTag();
    for (int i = 0; i < mTabs.size(); i++) {
        if (mTabs.get(i).equals(tabInfo)) {
            mPager.setCurrentItem(i);
        }
    }
}

@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}

public void replace(final int position, final Class fragmentClass, final Bundle args) {
    /* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();
}

/**
 * Updates the tabs.
 *
 * @param tabInfo
 *          the new tab info
 * @param position
 *          the position
 */
private void updateTabs(final TabInfo tabInfo, final int position) {
    mTabs.remove(position);
    mTabs.add(position, tabInfo);
    mActionBar.getTabAt(position).setTag(tabInfo);
}

/**
 * Creates the history using the current state.
 */
public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

/**
 * Called on back
 */
public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

/**
 * Returns if the history is empty.
 *
 * @param position
 *          the position
 * @return  the flag if empty
 */
private boolean historyIsEmpty(final int position) {
    return history == null || history.isEmpty() || history.get(position).isEmpty();
}

private boolean isLastItemInHistory(final int position) {
    return history.get(position).size() == 1;
}

/**
 * Returns the previous state by the position provided.
 *
 * @param position
 *          the position
 * @return  the tab info
 */
private TabInfo getPrevious(final int position) {
    TabInfo currentTabInfo = history.get(position).pop();
    if (!history.get(position).isEmpty()) {
        currentTabInfo = history.get(position).peek();
    }
    return currentTabInfo;
}

/** The tab info class */
private static class TabInfo {

    /** The fragment class. */
    public Class fragmentClass;

    /** The args.*/
    public Bundle args;

    /**
     * Creates a new instance.
     *
     * @param fragmentClass
     *          the fragment class
     * @param args
     *          the args
     */
    public TabInfo(Class fragmentClass, Bundle args) {
        this.fragmentClass = fragmentClass;
        this.args = args;
    }

    @Override
    public boolean equals(final Object o) {
        return this.fragmentClass.getName().equals(o.getClass().getName());
    }

    @Override
    public int hashCode() {
        return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
    }

    @Override
    public String toString() {
        return "TabInfo{" +
                "fragmentClass=" + fragmentClass +
                '}';
    }
}

La toute première fois que vous ajoutez tous les onglets, nous devons appeler la méthode createHistory (), pour créer l'historique initial

public void createHistory() {
    int position = 0;
    TOTAL_TABS = mTabs.size();
    for (TabInfo mTab : mTabs) {
        if (history.get(position) == null) {
            history.put(position, new Stack<TabInfo>());
        }
        history.get(position).push(new TabInfo(mTab.fragmentClass, mTab.args));
        position++;
    }
}

Chaque fois que vous souhaitez remplacer un fragment par un onglet spécifique que vous appelez: replace (position int finale, classe Class fragmentClass finale, arguments Bundle finaux)

/* Save the fragment to the history. */
    mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();

    /* Update the tabs. */
    updateTabs(new TabInfo(fragmentClass, args), position);

    /* Updates the history. */
    history.get(position).push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));

    notifyDataSetChanged();

À l'arrière, vous devez appeler la méthode back ():

public void back() {
    int position = mActionBar.getSelectedTab().getPosition();
    if (!historyIsEmpty(position)) {
        /* In case there is not any other item in the history, then finalize the activity. */
        if (isLastItemInHistory(position)) {
            mActivity.finish();
        }
        final TabInfo currentTabInfo = getPrevious(position);
        mTabs.clear();
        for (int i = 0; i < TOTAL_TABS; i++) {
            if (i == position) {
                mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
            } else {
                TabInfo otherTabInfo = history.get(i).peek();
                mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
            }
        }
    }
    mActionBar.selectTab(mActionBar.getTabAt(position));
    notifyDataSetChanged();
}

La solution fonctionne avec la barre d'action sherlock et avec un geste de balayage.

mspapant
la source
c'est bien! pourriez-vous le mettre sur le github?
Nicramus
Cette implémentation est la plus propre de toutes celles fournies ici, et résout chaque problème. Ce devrait être la réponse acceptée.
dazedviper
Quelqu'un at-il eu des problèmes avec cette mise en œuvre? J'en ai 2. 1. le premier dos ne s'enregistre pas - je dois cliquer deux fois en arrière pour qu'il s'enregistre. 2. Après avoir cliqué en arrière (deux fois) sur l'onglet a et je vais à l'onglet b, quand je reviens à l'onglet a, il montre simplement le contenu de l'onglet b
yaronbic
7

tl; dr: utilisez un fragment d'hôte qui est responsable du remplacement de son contenu hébergé et garde une trace de l'historique de navigation (comme dans un navigateur).

Comme votre cas d'utilisation se compose d'un nombre fixe d'onglets, ma solution fonctionne bien: L'idée est de remplir le ViewPager avec des instances d'une classe personnalisée HostFragment, capable de remplacer son contenu hébergé et de conserver son propre historique de navigation. Pour remplacer le fragment hébergé, vous appelez la méthode hostfragment.replaceFragment():

public void replaceFragment(Fragment fragment, boolean addToBackstack) {
    if (addToBackstack) {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
    } else {
        getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
    }
}

Tout ce que cette méthode fait est de remplacer la disposition du cadre par l'id R.id.hosted_fragmentavec le fragment fourni à la méthode.

Consultez mon tutoriel sur ce sujet pour plus de détails et un exemple de travail complet sur GitHub!

marktani
la source
Bien que cela semble plus maladroit, je pense que c'est en fait la meilleure façon (en termes de structure et de facilité de maintenance) de le faire.
Sira Lam
Je finis par débuter un NPE. : / Impossible de trouver la vue à remplacer
Fariz Fakkel
6

Certaines des solutions présentées m'ont beaucoup aidé à résoudre partiellement le problème, mais il y a encore une chose importante manquante dans les solutions qui a produit des exceptions inattendues et du contenu de page noire au lieu de fragmenter le contenu dans certains cas.

Le fait est que la classe FragmentPagerAdapter utilise l'ID d'élément pour stocker les fragments mis en cache dans FragmentManager . Pour cette raison, vous devez également remplacer la méthode getItemId (int position) afin qu'elle renvoie par exemple la position pour les pages de niveau supérieur et la position 100+ pour les pages de détails. Sinon, le fragment de niveau supérieur précédemment créé serait renvoyé du cache au lieu d'un fragment de niveau détail.

De plus, je partage ici un exemple complet comment implémenter une activité de type onglet avec des pages de fragment à l' aide de ViewPager et des boutons d'onglet à l'aide de RadioGroup qui permet le remplacement des pages de niveau supérieur par des pages détaillées et prend également en charge le bouton de retour. Cette implémentation prend en charge un seul niveau d'empilement arrière (liste d'éléments - détails des éléments) mais l'implémentation d'empilement arrière à plusieurs niveaux est simple. Cet exemple fonctionne assez bien dans les cas normaux, sauf qu'il lance une exception NullPointerException au cas où vous passez par exemple à la deuxième page, changez le fragment de la première page (bien qu'il ne soit pas visible) et revenez à la première page. Je publierai une solution à ce problème une fois que j'aurai compris:

public class TabsActivity extends FragmentActivity {

  public static final int PAGE_COUNT = 3;
  public static final int FIRST_PAGE = 0;
  public static final int SECOND_PAGE = 1;
  public static final int THIRD_PAGE = 2;

  /**
   * Opens a new inferior page at specified tab position and adds the current page into back
   * stack.
   */
  public void startPage(int position, Fragment content) {
    // Replace page adapter fragment at position.
    mPagerAdapter.start(position, content);
  }

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

    // Initialize basic layout.
    this.setContentView(R.layout.tabs_activity);

    // Add tab fragments to view pager.
    {
      // Create fragments adapter.
      mPagerAdapter = new PagerAdapter(pager);
      ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
      pager.setAdapter(mPagerAdapter);

      // Update active tab in tab bar when page changes.
      pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int index, float value, int nextIndex) {
          // Not used.
        }

        @Override
        public void onPageSelected(int index) {
          RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
            R.id.tabs_radio_group);
          switch (index) {
            case 0: {
              tabs_radio_group.check(R.id.first_radio_button);
            }
            break;
            case 1: {
              tabs_radio_group.check(R.id.second_radio_button);
            }
            break;
            case 2: {
              tabs_radio_group.check(R.id.third_radio_button);
            }
            break;
          }
        }

        @Override
        public void onPageScrollStateChanged(int index) {
          // Not used.
        }
      });
    }

    // Set "tabs" radio group on checked change listener that changes the displayed page.
    RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
    radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
      @Override
      public void onCheckedChanged(RadioGroup radioGroup, int id) {
        // Get view pager representing tabs.
        ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
        if (view_pager == null) {
          return;
        }

        // Change the active page.
        switch (id) {
          case R.id.first_radio_button: {
            view_pager.setCurrentItem(FIRST_PAGE);
          }
          break;
          case R.id.second_radio_button: {
            view_pager.setCurrentItem(SECOND_PAGE);
          }
          break;
          case R.id.third_radio_button: {
            view_pager.setCurrentItem(THIRD_PAGE);
          }
          break;
        }
      });
    }
  }

  @Override
  public void onBackPressed() {
    if (!mPagerAdapter.back()) {
      super.onBackPressed();
    }
  }

  /**
   * Serves the fragments when paging.
   */
  private class PagerAdapter extends FragmentPagerAdapter {

    public PagerAdapter(ViewPager container) {
      super(TabsActivity.this.getSupportFragmentManager());

      mContainer = container;
      mFragmentManager = TabsActivity.this.getSupportFragmentManager();

      // Prepare "empty" list of fragments.
      mFragments = new ArrayList<Fragment>(){};
      mBackFragments = new ArrayList<Fragment>(){};
      for (int i = 0; i < PAGE_COUNT; i++) {
        mFragments.add(null);
        mBackFragments.add(null);
      }
    }

    /**
     * Replaces the view pager fragment at specified position.
     */
    public void replace(int position, Fragment fragment) {
      // Get currently active fragment.
      Fragment old_fragment = mFragments.get(position);
      if (old_fragment == null) {
        return;
      }

      // Replace the fragment using transaction and in underlaying array list.
      // NOTE .addToBackStack(null) doesn't work
      this.startUpdate(mContainer);
      mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        .remove(old_fragment).add(mContainer.getId(), fragment)
        .commit();
      mFragments.set(position, fragment);
      this.notifyDataSetChanged();
      this.finishUpdate(mContainer);
    }

    /**
     * Replaces the fragment at specified position and stores the current fragment to back stack
     * so it can be restored by #back().
     */
    public void start(int position, Fragment fragment) {
      // Remember current fragment.
      mBackFragments.set(position, mFragments.get(position));

      // Replace the displayed fragment.
      this.replace(position, fragment);
    }

    /**
     * Replaces the current fragment by fragment stored in back stack. Does nothing and returns
     * false if no fragment is back-stacked.
     */
    public boolean back() {
      int position = mContainer.getCurrentItem();
      Fragment fragment = mBackFragments.get(position);
      if (fragment == null) {
        // Nothing to go back.
        return false;
      }

      // Restore the remembered fragment and remove it from back fragments.
      this.replace(position, fragment);
      mBackFragments.set(position, null);
      return true;
    }

    /**
     * Returns fragment of a page at specified position.
     */
    @Override
    public Fragment getItem(int position) {
      // If fragment not yet initialized, create its instance.
      if (mFragments.get(position) == null) {
        switch (position) {
          case FIRST_PAGE: {
            mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
          }
          break;
          case SECOND_PAGE: {
            mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
          }
          break;
          case THIRD_PAGE: {
            mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
          }
          break;
        }
      }

      // Return fragment instance at requested position.
      return mFragments.get(position);
    }

    /**
     * Custom item ID resolution. Needed for proper page fragment caching.
     * @see FragmentPagerAdapter#getItemId(int).
     */
    @Override
    public long getItemId(int position) {
      // Fragments from second level page hierarchy have their ID raised above 100. This is
      // important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
      // this item ID key.
      Fragment item = mFragments.get(position);
      if (item != null) {
        if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
          (item instanceof NewThirdFragment)) {
          return 100 + position;
        }
      }

      return position;
    }

    /**
     * Returns number of pages.
     */
    @Override
    public int getCount() {
      return mFragments.size();
    }

    @Override
    public int getItemPosition(Object object)
    {
      int position = POSITION_UNCHANGED;
      if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
        if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
        if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
        if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
          position = POSITION_NONE;
        }
      }
      return position;
    }

    private ViewPager mContainer;
    private FragmentManager mFragmentManager;

    /**
     * List of page fragments.
     */
    private List<Fragment> mFragments;

    /**
     * List of page fragments to return to in onBack();
     */
    private List<Fragment> mBackFragments;
  }

  /**
   * Tab fragments adapter.
   */
  private PagerAdapter mPagerAdapter;
}
Blackhex
la source
Hmm, il y a un autre problème avec ce code. Lorsque je veux démarrer une activité à partir d'un fragment qui a été supprimé et ajouté aggain, j'obtiens une erreur: "État d'enregistrement d'échec: actif NewFirstFragment {405478a0} a effacé l'index: -1" (trace de pile complète: nopaste.info/547a23dfea.html ). En regardant les sources Android, je me rends compte que cela a quelque chose à voir avec le backstack mais je ne sais pas comment y remédier jusqu'à présent. Quelqu'un a-t-il une idée?
Blackhex
2
Cool, en utilisant <code> mFragmentManager.beginTransaction (). Detach (old_fragment) .attach (fragment) .commit (); </code> au lieu de <pre> mFragmentManager.beginTransaction (). SetTransition (FragmentTransaction.TRANSIT_FRAGMENT_OPEN) .remove ( old_fragment) .add (mContainer.getId (), fragment) .commit (); </pre> dans l'exemple ci-dessus semble résoudre les deux problèmes :-).
Blackhex
1
Si vous utilisez cette méthode, assurez-vous de ne pas laisser Android recréer votre activité lors de la rotation de l'appareil. Sinon, le PagerAdapter sera recréé et vous perdrez vos mBackFragments. Cela confondra également sérieusement le FragmentManager. L'utilisation de BackStack évite ce problème, au détriment d'une implémentation plus compliquée de PagerAdapter (c'est-à-dire que vous ne pouvez pas hériter de FragmentPagerAdapter.)
Norman
6

J'ai créé un ViewPager avec 3 éléments et 2 sous-éléments pour les index 2 et 3 et voici ce que je voulais faire ..

entrez la description de l'image ici

J'ai implémenté cela avec l'aide des questions et réponses précédentes de StackOverFlow et voici le lien.

ViewPagerChildFragments

Rukmal Dias
la source
Votre projet échoue si vous faites pivoter l'appareil.
Dory
6

Pour remplacer un fragment dans un ViewPagervous pouvez déplacer les codes sources de ViewPager, PagerAdapteret les FragmentStatePagerAdapterclasses dans votre projet et ajoutez le code suivant.

en ViewPager:

public void notifyItemChanged(Object oldItem, Object newItem) {
    if (mItems != null) {
            for (ItemInfo itemInfo : mItems) {
                        if (itemInfo.object.equals(oldItem)) {
                                itemInfo.object = newItem;
                        }
                    }
       }
       invalidate();
    }

dans FragmentStatePagerAdapter:

public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
       startUpdate(container);

       // remove old fragment

       if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
       int position = getFragmentPosition(oldFragment);
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, null);
        mFragments.set(position, null);

        mCurTransaction.remove(oldFragment);

        // add new fragment

        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        mFragments.set(position, newFragment);
        mCurTransaction.add(container.getId(), newFragment);

       finishUpdate(container);

       // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
       handleGetItemInbalidated(container, oldFragment, newFragment);

       container.notifyItemChanged(oldFragment, newFragment);
    }

protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int  getFragmentPosition(Fragment fragment);

handleGetItemInvalidated()garantit qu'après son prochain appel, getItem()newFragment getFragmentPosition()renvoie la position du fragment dans votre adaptateur.

Maintenant, pour remplacer les fragments, appelez

mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);

Si vous êtes intéressé par un exemple de projet, demandez-moi les sources.

AndroidTeam chez Mail.Ru
la source
À quoi devraient ressembler les méthodes getFragmentPosition () et handleGetItemInbalidated ()? Je ne peux pas mettre à jour getItem () pour retourner NewFragment .. Aidez
Salut! Veuillez trouver le projet de test sur ce lien files.mail.ru/eng?back=%2FEZU6G4
AndroidTeam At Mail.Ru
1
Votre projet de test échoue si vous faites pivoter l'appareil.
seb
1
@ AndroidTeamAtMail.Ru le lien semble expiré.Veuillez partager le code
Sunny
Vous pouvez voir ma réponse . Il remplace les fragments et revient au premier.
Lyd
4

Fonctionne très bien avec la solution d'AndroidTeam, mais j'ai trouvé que j'avais besoin de pouvoir revenir en arrière, FrgmentTransaction.addToBackStack(null) mais simplement ajouter cela ne fera que remplacer le fragment sans avertir le ViewPager. La combinaison de la solution fournie avec cette amélioration mineure vous permettra de revenir à l'état précédent en remplaçant simplement la onBackPressed()méthode de l'activité . Le plus gros inconvénient est qu'il ne reviendra qu'un par un, ce qui peut entraîner plusieurs clics en arrière

private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
private ArrayList<Integer> bPosition = new ArrayList<Integer>();

public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
    startUpdate(container);

    // remove old fragment

    if (mCurTransaction == null) {
         mCurTransaction = mFragmentManager.beginTransaction();
     }
    int position = getFragmentPosition(oldFragment);
     while (mSavedState.size() <= position) {
         mSavedState.add(null);
     }

     //Add Fragment to Back List
     bFragments.add(oldFragment);

     //Add Pager Position to Back List
     bPosition.add(position);

     mSavedState.set(position, null);
     mFragments.set(position, null);

     mCurTransaction.remove(oldFragment);

     // add new fragment

     while (mFragments.size() <= position) {
         mFragments.add(null);
     }
     mFragments.set(position, newFragment);
     mCurTransaction.add(container.getId(), newFragment);

    finishUpdate(container);

    // ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
    handleGetItemInvalidated(container, oldFragment, newFragment);

    container.notifyItemChanged(oldFragment, newFragment);
 }


public boolean popBackImmediate(ViewPager container){
    int bFragSize = bFragments.size();
    int bPosSize = bPosition.size();

    if(bFragSize>0 && bPosSize>0){
        if(bFragSize==bPosSize){
            int last = bFragSize-1;
            int position = bPosition.get(last);

            //Returns Fragment Currently at this position
            Fragment replacedFragment = mFragments.get(position);               
            Fragment originalFragment = bFragments.get(last);

            this.replaceFragments(container, replacedFragment, originalFragment);

            bPosition.remove(last);
            bFragments.remove(last);

            return true;
        }
    }

    return false;       
}

J'espère que cela aide quelqu'un.

Aussi loin que getFragmentPosition()cela se passe, c'est à peu près getItem()à l'envers. Vous savez quels fragments vont où, assurez-vous de renvoyer la position correcte dans laquelle ils se trouveront. Voici un exemple:

    @Override
    protected int getFragmentPosition(Fragment fragment) {
            if(fragment.equals(originalFragment1)){
                return 0;
            }
            if(fragment.equals(replacementFragment1)){
                return 0;
            }
            if(fragment.equals(Fragment2)){
                return 1;
            }
        return -1;
    }
connaisseur
la source
3

Dans votre onCreateViewméthode, containerest en fait une ViewPagerinstance.

Donc, juste appeler

ViewPager vpViewPager = (ViewPager) container;
vpViewPager.setCurrentItem(1);

changera le fragment actuel dans votre ViewPager.

Ivan Bajalovic
la source
1
Après des heures de recherche, c'était cette seule ligne de code dont j'avais besoin pour que tous mes onglets fonctionnent correctement. vpViewPager.setCurrentItem(1);. J'ai parcouru l'exemple de chaque personne, sans que rien ne se passe à chaque fois jusqu'à ce que j'arrive enfin au vôtre. Je vous remercie.
Brandon
1
époustouflé ... c'est incroyable !! Je ne savais pas que la variable conteneur était le viseur lui-même. Cette réponse doit être explorée avant toutes les autres longues. De plus, aucune variable d'écoute n'est transmise au constructeur de fragments, ce qui simplifie toute l'implémentation lorsque Android recrée automatiquement des fragments à l'aide du constructeur sans argument par défaut lorsque la configuration change.
Kevin Lee
3
n'est-ce pas simplement le passage à un onglet existant? comment est-ce lié au remplacement des fragments?
behelit
2
D'accord avec @behelit, cela ne répond pas à la question, cela va simplement déplacer le ViewPagersur une autre page, il ne remplacera aucun fragment.
Yoann Hercouet
2

Voici ma solution relativement simple à ce problème. Les clés de cette solution sont à utiliser FragmentStatePagerAdapterau lieu de, FragmentPagerAdaptercar la première supprimera les fragments inutilisés pour vous tandis que la dernière conserve toujours leurs instances. La seconde est l'utilisation de POSITION_NONEdans getItem (). J'ai utilisé une simple liste pour garder une trace de mes fragments. Mon exigence était de remplacer la liste complète des fragments à la fois par une nouvelle liste, mais celle-ci pourrait être facilement modifiée pour remplacer les fragments individuels:

public class MyFragmentAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> fragmentList = new ArrayList<Fragment>();
    private List<String> tabTitleList = new ArrayList<String>();

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

    public void addFragments(List<Fragment> fragments, List<String> titles) {
        fragmentList.clear();
        tabTitleList.clear();
        fragmentList.addAll(fragments);
        tabTitleList.addAll(titles);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        if (fragmentList.contains(object)) {
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int item) {
        if (item >= fragmentList.size()) {
            return null;
        }
        return fragmentList.get(item);
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return tabTitleList.get(position);
    }
}
Chris Knight
la source
2

J'ai également fait une solution, qui fonctionne avec Stacks . C'est une approche plus modulaire , donc vous n'avez pas besoin de spécifier chaque fragment et fragment de détail dans votre FragmentPagerAdapter. Il s'appuie sur l'exemple d'ActionbarSherlock qui dérive si j'ai raison de l'application de démonstration Google.

/**
 * This is a helper class that implements the management of tabs and all
 * details of connecting a ViewPager with associated TabHost.  It relies on a
 * trick.  Normally a tab host has a simple API for supplying a View or
 * Intent that each tab will show.  This is not sufficient for switching
 * between pages.  So instead we make the content part of the tab host
 * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
 * view to show as the tab content.  It listens to changes in tabs, and takes
 * care of switch to the correct paged in the ViewPager whenever the selected
 * tab changes.
 * 
 * Changed to support more Layers of fragments on each Tab.
 * by sebnapi (2012)
 * 
 */
public class TabsAdapter extends FragmentPagerAdapter
        implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
    private final Context mContext;
    private final TabHost mTabHost;
    private final ViewPager mViewPager;

    private ArrayList<String> mTabTags = new ArrayList<String>();
    private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();

    static final class TabInfo {
        public final String tag;
        public final Class<?> clss;
        public Bundle args;

        TabInfo(String _tag, Class<?> _class, Bundle _args) {
            tag = _tag;
            clss = _class;
            args = _args;
        }
    }

    static class DummyTabFactory implements TabHost.TabContentFactory {
        private final Context mContext;

        public DummyTabFactory(Context context) {
            mContext = context;
        }

        @Override
        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

    public interface SaveStateBundle{
        public Bundle onRemoveFragment(Bundle outState);
    }

    public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mTabHost = tabHost;
        mViewPager = pager;
        mTabHost.setOnTabChangedListener(this);
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    /**
     * Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
     * addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
     * The Stack will hold always the default Fragment u add here.
     * 
     * DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
     * beahvior.
     * 
     * @param tabSpec
     * @param clss
     * @param args
     */
    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
        Stack<TabInfo> tabStack = new Stack<TabInfo>();

        tabSpec.setContent(new DummyTabFactory(mContext));
        mTabHost.addTab(tabSpec);
        String tag = tabSpec.getTag();
        TabInfo info = new TabInfo(tag, clss, args);

        mTabTags.add(tag);                  // to know the position of the tab tag 
        tabStack.add(info);
        mTabStackMap.put(tag, tabStack);
        notifyDataSetChanged();
    }

    /**
     * Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
     * it will be instantiated by this object. Proivde _args for your Fragment.
     * 
     * @param fm
     * @param _tag
     * @param _class
     * @param _args
     */
    public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
        TabInfo info = new TabInfo(_tag, _class, _args);
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
        if(frag instanceof SaveStateBundle){
            Bundle b = new Bundle();
            ((SaveStateBundle) frag).onRemoveFragment(b);
            tabStack.peek().args = b;
        }
        tabStack.add(info);
        FragmentTransaction ft = fm.beginTransaction();
        ft.remove(frag).commit();
        notifyDataSetChanged();
    }

    /**
     * Will pop the Fragment added to the Tab with the Tag _tag
     * 
     * @param fm
     * @param _tag
     * @return
     */
    public boolean popFragment(FragmentManager fm, String _tag){
        Stack<TabInfo> tabStack = mTabStackMap.get(_tag);   
        if(tabStack.size()>1){
            tabStack.pop();
            Fragment frag = fm.findFragmentByTag("android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
            FragmentTransaction ft = fm.beginTransaction();
            ft.remove(frag).commit();
            notifyDataSetChanged();
            return true;
        }
        return false;
    }

    public boolean back(FragmentManager fm) {
        int position = mViewPager.getCurrentItem();
        return popFragment(fm, mTabTags.get(position));
    }

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

    @Override
    public int getItemPosition(Object object) {
        ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
        for(Stack<TabInfo> tabStack: mTabStackMap.values()){
            positionNoneHack.add(tabStack.peek().clss);
        }   // if the object class lies on top of our stacks, we return default
        if(positionNoneHack.contains(object.getClass())){
            return POSITION_UNCHANGED;
        }
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
        TabInfo info = tabStack.peek();
        return Fragment.instantiate(mContext, info.clss.getName(), info.args);
    }

    @Override
    public void onTabChanged(String tabId) {
        int position = mTabHost.getCurrentTab();
        mViewPager.setCurrentItem(position);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        // Unfortunately when TabHost changes the current tab, it kindly
        // also takes care of putting focus on it when not in touch mode.
        // The jerk.
        // This hack tries to prevent this from pulling focus out of our
        // ViewPager.
        TabWidget widget = mTabHost.getTabWidget();
        int oldFocusability = widget.getDescendantFocusability();
        widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        mTabHost.setCurrentTab(position);
        widget.setDescendantFocusability(oldFocusability);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

}

Ajoutez ceci pour la fonctionnalité du bouton de retour dans votre MainActivity:

@Override
public void onBackPressed() {
  if (!mTabsAdapter.back(getSupportFragmentManager())) {
    super.onBackPressed();
  }
}

Si vous souhaitez enregistrer l'état du fragment lorsqu'il est supprimé. Laissez votre fragment implémenter l'interface SaveStateBundlerenvoyer dans la fonction un ensemble avec votre état de sauvegarde. Obtenez le bundle après instanciation par this.getArguments().

Vous pouvez instancier un onglet comme celui-ci:

mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
                FirstFragmentActivity.FirstFragmentFragment.class, null);

fonctionne de façon similaire si vous voulez ajouter un fragment au-dessus d'une pile d'onglets. Important : Je pense que cela ne fonctionnera pas si vous voulez avoir 2 instances de la même classe au-dessus de deux onglets. J'ai fait cette solution rapidement ensemble, donc je ne peux que la partager sans fournir aucune expérience avec elle.

Seb
la source
J'obtiens une exception nullpointer en appuyant sur le bouton de retour et mon fragment remplacé n'est pas visible non plus. Alors peut-être que je fais quelque chose de mal: quel devrait être l'appel correct pour changer correctement les fragments?
Jeroen
Mise à jour: j'ai corrigé la plupart des problèmes, cependant, si j'appuie sur un bouton de retour après avoir ajouté un nouveau fragment, puis que je passe à un autre onglet et vice-versa, j'obtiens une exception nullpointer. J'ai donc 2 questions: 1: comment puis-je supprimer l'historique d'un onglet et m'assurer que seul le dernier fragment (haut de la pile) est toujours présent? 2: comment puis-je me débarrasser de l'exception nullpointer?
Jeroen
@Jeroen 1: vous pouvez obtenir la bonne pile de mTabStackMap, puis supprimer tous les objets TabInfo qui se trouvent sous le haut de la pile, 2: très difficile à dire, posez une question avec le stacktrace, puis je pourrais essayer de savoir, sont u sûr que ce n'est pas un échec dans votre code? Cela se produit-il également si vous passez à un onglet (puis à l'arrière) qui se trouve dans le voisinage direct (à droite ou à gauche)?
seb
Ok, j'ai rencontré un nouveau problème: si je fais pivoter mon écran, alors la pile semble à nouveau vide? Il semble perdre la trace des éléments précédents ...
Jeroen
1
@seb merci. J'utilisais ce code mais des fragments imbriqués sont maintenant possibles donc ce n'est plus un problème. :)
Garcon
2

Le remplacement de fragments dans un viewpager est assez compliqué mais est très possible et peut sembler super lisse. Tout d'abord, vous devez laisser le viseur lui-même gérer la suppression et l'ajout des fragments. Ce qui se passe, c'est lorsque vous remplacez le fragment à l'intérieur de SearchFragment, votre viewpager conserve ses vues de fragment. Vous vous retrouvez donc avec une page vierge car le SearchFragment est supprimé lorsque vous essayez de le remplacer.

La solution consiste à créer un écouteur à l'intérieur de votre viewpager qui gérera les modifications effectuées à l'extérieur de celui-ci, ajoutez d'abord ce code au bas de votre adaptateur.

public interface nextFragmentListener {
    public void fragment0Changed(String newFragmentIdentification);
}

Ensuite, vous devez créer une classe privée dans votre viewpager qui devient un écouteur lorsque vous souhaitez modifier votre fragment. Par exemple, vous pouvez ajouter quelque chose comme ça. Notez qu'il implémente l'interface qui vient d'être créée. Ainsi, chaque fois que vous appelez cette méthode, elle exécutera le code à l'intérieur de la classe ci-dessous.

private final class fragmentChangeListener implements nextFragmentListener {


    @Override
    public void fragment0Changed(String fragment) {
        //I will explain the purpose of fragment0 in a moment
        fragment0 = fragment;
        manager.beginTransaction().remove(fragAt0).commit();

        switch (fragment){
            case "searchFragment":
                fragAt0 = SearchFragment.newInstance(listener);
                break;
            case "searchResultFragment":
                fragAt0 = Fragment_Table.newInstance(listener);
                break;
        }

        notifyDataSetChanged();
    }

Il y a deux choses principales à souligner ici:

  1. fragAt0 est un fragment "flexible". Il peut prendre n'importe quel type de fragment que vous lui donnez. Cela lui permet de devenir votre meilleur ami en changeant le fragment en position 0 en le fragment que vous désirez.
  2. Remarquez les écouteurs placés dans le constructeur 'newInstance (écouteur). Voici comment vous appellerezfragment0Changed (String newFragmentIdentification) `. Le code suivant montre comment vous créez l'écouteur à l'intérieur de votre fragment.

    static nextFragmentListener listenerSearch;

        public static Fragment_Journals newInstance(nextFragmentListener listener){
                listenerSearch = listener;
                return new Fragment_Journals();
            }

Vous pourriez appeler le changement à l'intérieur de votre onPostExecute

private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{

    protected Void doInBackground(Void... params){
        .
        .//some more operation
        .
    }
    protected void onPostExecute(Void param){

        listenerSearch.fragment0Changed("searchResultFragment");
    }

}

Cela déclencherait le code à l'intérieur de votre viewpager pour commuter votre fragment à la position zéro fragAt0 pour devenir un nouveau searchResultFragment. Il y a deux autres petits morceaux que vous devez ajouter au viseur avant qu'il ne devienne fonctionnel.

L'une serait dans la méthode de remplacement getItem du viewpager.

@Override
public Fragment getItem(int index) {

    switch (index) {
    case 0:
        //this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.  

        if(fragAt0 == null){

            switch(fragment0){
            case "searchFragment":
                fragAt0 = FragmentSearch.newInstance(listener);
                break;
            case "searchResultsFragment":
                fragAt0 = FragmentSearchResults.newInstance(listener);
                break;
            }
        }
        return fragAt0;
    case 1:
        // Games fragment activity
        return new CreateFragment();

    }

Maintenant, sans cette dernière pièce, vous obtiendrez toujours une page blanche. Un peu boiteux, mais c'est une partie essentielle du viewPager. Vous devez remplacer la méthode getItemPosition du viewpager. Normalement, cette méthode retournera POSITION_UNCHANGED qui indique au viewpager de garder tout le même et ainsi getItem ne sera jamais appelé pour placer le nouveau fragment sur la page. Voici un exemple de quelque chose que vous pourriez faire

public int getItemPosition(Object object)
{
    //object is the current fragment displayed at position 0.  
    if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
        return POSITION_NONE;
    //this condition is for when you press back
    }else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
        return POSITION_NONE;
    }
        return POSITION_UNCHANGED
}

Comme je l'ai dit, le code est très impliqué, mais vous devez essentiellement créer un adaptateur personnalisé pour votre situation. Les choses que j'ai mentionnées permettront de changer le fragment. Il faudra probablement beaucoup de temps pour tout tremper, donc je serais patient, mais tout cela aura du sens. Cela vaut vraiment la peine de prendre le temps car cela peut rendre une application très élégante.

Voici le nugget pour gérer le bouton de retour. Vous mettez cela dans votre MainActivity

 public void onBackPressed() {
    if(mViewPager.getCurrentItem() == 0) {
        if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
            ((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
        }else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
            finish();
        }
    }

Vous devrez créer une méthode appelée backPressed () à l'intérieur de FragmentSearchResults qui appelle fragment0changed. Ceci en tandem avec le code que j'ai montré précédemment gérera la pression sur le bouton de retour. Bonne chance avec votre code pour changer le viseur. Cela prend beaucoup de travail, et pour autant que je l'ai trouvé, il n'y a pas d'adaptations rapides. Comme je l'ai dit, vous créez essentiellement un adaptateur de viseur personnalisé et le laissez gérer toutes les modifications nécessaires à l'aide d'écouteurs

user3331142
la source
Merci pour cette solution. Mais avez-vous eu des problèmes après avoir tourné l'écran. Mon application se bloque lorsque je fais pivoter l'écran et tente de recharger un autre fragment dans le même onglet.
AnteGemini
Cela fait un moment que je n'ai pas fait de développement Android, mais je pense qu'il y a une donnée qui n'est plus là sur laquelle votre application compte. Lorsque vous effectuez une rotation, toute l'activité redémarre et peut provoquer des plantages si vous essayez de recharger votre état précédent sans toutes les données dont vous avez besoin (par exemple, à partir du bundle dans onPause, onResume)
user3331142
2

C'est ma façon d'y parvenir.

Tout d'abord, ajoutez Root_fragmentl' viewPageronglet intérieur dans lequel vous souhaitez implémenter l' fragmentévénement de clic de bouton . Exemple;

@Override
public Fragment getItem(int position) {
  if(position==0)
      return RootTabFragment.newInstance();
  else
      return SecondPagerFragment.newInstance();
}

Tout d'abord, RootTabFragmentdevrait être inclus FragmentLayoutpour le changement de fragment.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/root_frame"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</FrameLayout>

Ensuite, à l'intérieur RootTabFragment onCreateView, implémentez fragmentChangepour votreFirstPagerFragment

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();

Après cela, implémentez l' onClickévénement pour votre bouton à l'intérieur FirstPagerFragmentet modifiez à nouveau le fragment comme ça.

getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();

J'espère que cela vous aidera.

Min Khant Lu
la source
1

J'ai trouvé une solution simple, qui fonctionne bien même si vous souhaitez ajouter de nouveaux fragments au milieu ou remplacer le fragment actuel. Dans ma solution, vous devez remplacer getItemId()ce qui devrait retourner un identifiant unique pour chaque fragment. Ne pas positionner comme par défaut.

Ça y est:

public class DynamicPagerAdapter extends FragmentPagerAdapter {

private ArrayList<Page> mPages = new ArrayList<Page>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

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

public void replacePage(int position, Page page) {
    mPages.set(position, page);
    notifyDataSetChanged();
}

public void setPages(ArrayList<Page> pages) {
    mPages = pages;
    notifyDataSetChanged();
}

@Override
public Fragment getItem(int position) {
    if (mPages.get(position).mPageType == PageType.FIRST) {
        return FirstFragment.newInstance(mPages.get(position));
    } else {
        return SecondFragment.newInstance(mPages.get(position));
    }
}

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

@Override
public long getItemId(int position) {
    // return unique id
    return mPages.get(position).getId();
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    while (mFragments.size() <= position) {
        mFragments.add(null);
    }
    mFragments.set(position, fragment);
    return fragment;
}

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

@Override
public int getItemPosition(Object object) {
    PagerFragment pagerFragment = (PagerFragment) object;
    Page page = pagerFragment.getPage();
    int position = mFragments.indexOf(pagerFragment);
    if (page.equals(mPages.get(position))) {
        return POSITION_UNCHANGED;
    } else {
        return POSITION_NONE;
    }
}
}

Remarque: Dans cet exemple FirstFragmentet SecondFragmentétend la classe abstraite PageFragment, qui a une méthode getPage().

Sergei Vasilenko
la source
1

Je fais quelque chose de similaire à wize mais dans ma réponse, vous pouvez changer entre les deux fragments quand vous le souhaitez. Et avec la réponse wize, j'ai des problèmes lorsque je change l'orientation de l'écran et des choses comme ça. Voici à quoi ressemble le PagerAdapter:

    public class MyAdapter extends FragmentPagerAdapter
{
    static final int NUM_ITEMS = 2;
    private final FragmentManager mFragmentManager;
    private Fragment mFragmentAtPos0;
     private Map<Integer, String> mFragmentTags;
     private boolean isNextFragment=false;

    public MyAdapter(FragmentManager fm)
    {
        super(fm);
        mFragmentManager = fm;
         mFragmentTags = new HashMap<Integer, String>();
    }

    @Override
    public Fragment getItem(int position)
    {
        if (position == 0)
        {


            if (isPager) {
                mFragmentAtPos0 = new FirstPageFragment();
            } else {
                mFragmentAtPos0 = new NextFragment();
            }
            return mFragmentAtPos0;
        }
        else
            return SecondPageFragment.newInstance();
    }

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


 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }


    public void onChange(boolean isNextFragment) {

        if (mFragmentAtPos0 == null)
            mFragmentAtPos0 = getFragment(0);
        if (mFragmentAtPos0 != null)
            mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();


        if (!isNextFragment) {
            mFragmentAtFlashcards = new FirstPageFragment();
        } else {
            mFragmentAtFlashcards = new NextFragment();
        }

        notifyDataSetChanged();


    }


    @Override
    public int getItemPosition(Object object)
    {
        if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
            return POSITION_NONE;
         if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
            return POSITION_NONE;
        return POSITION_UNCHANGED;
    }


    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }
}

L'écouteur que j'ai implémenté dans l'activité de conteneur d'adaptateur pour le mettre dans le fragment lors de son attachement, voici l'activité:

    public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {

//...

  @Override
    public void onChange(boolean isNextFragment) {
        if (pagerAdapter != null)
            pagerAdapter.onChange(isNextFragment);


    }

//...
}

Puis dans le fragment mettant l'auditeur lors de l'attachement d'un appelant:

public class FirstPageFragment extends Fragment{


private ChangeFragmentListener changeFragmentListener;


//...
 @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        changeFragmentListener = ((PagerContainerActivity) activity);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        changeFragmentListener = null;
    }
//...
//in the on click to change the fragment
changeFragmentListener.onChange(true);
//...
}

Et enfin l'auditeur:

public interface changeFragmentListener {

    void onChange(boolean isNextFragment);

}
Jachumbelechao Unto Mantekilla
la source
1

J'ai suivi les réponses de @wize et @mdelolmo et j'ai obtenu la solution. Merci beaucoup. Mais, j'ai un peu réglé ces solutions pour améliorer la consommation de mémoire.

Problèmes que j'ai observés:

Ils enregistrent l'instance Fragmentqui est remplacée. Dans mon cas, c'est un fragment qui tient MapViewet j'ai pensé que c'était cher. Donc, je maintiens le FragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)lieu de Fragmentlui - même.

Voici ma mise en œuvre.

  public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {

    private SwitchFragListener mSwitchFragListener;
    private Switch mToggle;
    private int pagerAdapterPosChanged = POSITION_UNCHANGED;
    private static final int TOGGLE_ENABLE_POS = 2;


    public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
        super(fm);
        mToggle = toggle;

        mSwitchFragListener = new SwitchFragListener();
        mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                mSwitchFragListener.onSwitchToNextFragment();
            }
        });
    }

    @Override
    public Fragment getItem(int i) {
        switch (i)
        {
            case TOGGLE_ENABLE_POS:
                if(mToggle.isChecked())
                {
                    return TabReplaceFragment.getInstance();
                }else
                {
                    return DemoTab2Fragment.getInstance(i);
                }

            default:
                return DemoTabFragment.getInstance(i);
        }
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return "Tab " + (position + 1);
    }

    @Override
    public int getItemPosition(Object object) {

        //  This check make sures getItem() is called only for the required Fragment
        if (object instanceof TabReplaceFragment
                ||  object instanceof DemoTab2Fragment)
            return pagerAdapterPosChanged;

        return POSITION_UNCHANGED;
    }

    /**
     * Switch fragments Interface implementation
     */
    private final class SwitchFragListener implements
            SwitchFragInterface {

        SwitchFragListener() {}

        public void onSwitchToNextFragment() {

            pagerAdapterPosChanged = POSITION_NONE;
            notifyDataSetChanged();
        }
    }

    /**
     * Interface to switch frags
     */
    private interface SwitchFragInterface{
        void onSwitchToNextFragment();
    }
}

Lien de démonstration ici .. https://youtu.be/l_62uhKkLyM

À des fins de démonstration, utilisé 2 fragments TabReplaceFragmentet DemoTab2Fragmenten position deux. Dans tous les autres cas, j'utilise des DemoTabFragmentinstances.

Explication:

Je passe Switchd'Activité à DemoCollectionPagerAdapter. En fonction de l'état de ce commutateur, nous afficherons le fragment correct. Lorsque la vérification du commutateur est modifiée, j'appelle la méthode SwitchFragListener's onSwitchToNextFragment, où je change la valeur de pagerAdapterPosChangedvariable en POSITION_NONE. En savoir plus sur POSITION_NONE . Cela invalidera le getItem et j'ai des logiques pour instancier le bon fragment là-bas. Désolé, si l'explication est un peu compliquée.

Encore une fois merci à @wize et @mdelolmo pour l'idée originale.

J'espère que cela vous sera utile. :)

Faites-moi savoir si cette mise en œuvre présente des défauts. Ce sera très utile pour mon projet.

sha
la source
0

après des recherches, j'ai trouvé une solution avec un code court. tout d'abord, créez une instance publique sur fragment et supprimez simplement votre fragment sur onSaveInstanceState si le fragment ne se recrée pas lors du changement d'orientation.

 @Override
public void onSaveInstanceState(Bundle outState) {
    if (null != mCalFragment) {
        FragmentTransaction bt = getChildFragmentManager().beginTransaction();
        bt.remove(mFragment);
        bt.commit();
    }
    super.onSaveInstanceState(outState);
}
JosephM
la source