Comment puis-je conserver l'état du fragment lorsqu'il est ajouté à la pile arrière?

160

J'ai écrit une activité factice qui bascule entre deux fragments. Lorsque vous passez de FragmentA à FragmentB, FragmentA est ajouté à la pile arrière. Cependant, lorsque je reviens à FragmentA (en appuyant sur le dos), un tout nouveau FragmentA est créé et l'état dans lequel il se trouvait est perdu. J'ai l'impression que je suis après la même chose que cette question, mais j'ai inclus un exemple de code complet pour aider à résoudre le problème:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Avec quelques messages de journal ajoutés:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Il n'appelle jamais FragmentA.onSaveInstanceState et crée un nouveau FragmentA lorsque vous répondez. Cependant, si je suis sur FragmentA et que je verrouille l'écran, FragmentA.onSaveInstanceState est appelé. Tellement bizarre ... ai-je tort de m'attendre à ce qu'un fragment ajouté à la pile arrière ne nécessite pas de recréation? Voici ce que disent les documents :

Alors que si vous appelez addToBackStack () lors de la suppression d'un fragment, le fragment est arrêté et sera repris si l'utilisateur revient.

Eric
la source
3
@ Jan-Henk Qu'en est-il des choses qui doivent être récupérées? Par exemple, la position de défilement d'un fichier ListView. On dirait beaucoup trop de sauts de cercle pour attacher un écouteur de défilement et mettre à jour une variable d'instance.
Jake Wharton
2
@JakeWharton Je suis d'accord que cela devrait être plus facile, mais pour autant que je sache, il n'y a aucun moyen de contourner cela car onCreateView est appelé lorsqu'un fragment est restauré à partir de la backstack. Mais je peux me tromper :)
Jan-Henk
1
onCreate n'est pas appelé. Donc, apparemment, il réutilise la même instance mais appelle à nouveau onCreateView? Boiteux. Je suppose que je peux simplement mettre en cache le résultat de onCreateView et simplement renvoyer la vue existante si onCreateView est à nouveau appelé.
Eric
1
Exactement ce que je cherchais depuis des heures. Pouvez-vous publier comment vous avez réalisé cela en utilisant la variable d'instance?
Uma
1
J'ai donc récemment lancé ma propre implémentation dans github.com/frostymarvelous/Folio et j'ai rencontré un problème. Je suis capable de créer environ 5 pages / fragments complexes avant de commencer à avoir des plantages OOM. C'est ce qui m'a conduit ici. Se cacher et montrer ne suffit tout simplement pas. Les vues sont trop lourdes en mémoire.
frostymarvelous

Réponses:

120

Si vous revenez à un fragment de la pile arrière, il ne recrée pas le fragment mais réutilise la même instance et commence par onCreateView()dans le cycle de vie du fragment, voir Cycle de vie du fragment .

Donc, si vous souhaitez stocker un état, vous devez utiliser des variables d'instance et ne pas vous en remettre onSaveInstanceState().

Jan-Henk
la source
32
La version actuelle de la documentation contredit cette affirmation. L'organigramme dit ce que vous déclarez, mais le texte dans la zone principale de la page indique que onCreateView () n'est appelé que la première fois que le fragment est affiché: developer.android.com/guide/components/fragments.html Je combat cela problème maintenant, et je ne vois aucune méthode appelée lors du retour d'un fragment de la backstack. (Android 4.2)
Colin M.
10
J'ai essayé de consigner son comportement. OnCreateView () est toujours appelé lorsque le fragment est affiché.
princepiero
4
@ColinM. Une solution au problème?
blizzard
9
Cela ne fonctionne pas pour moi. Mes variables d'instance sont nulles au retour au fragment! Comment puis-je sauvegarder l'état?
Don Rhummy
5
donc si nous ne devons pas relayer sur l'instance de sauvegarde, comment enregistrer l'état et les données des fragments?
Mahdi
80

Comparé à Apple UINavigationControlleret UIViewController, Google ne fonctionne pas bien dans l'architecture logicielle Android. Et le document d'Android sur Fragmentn'aide pas beaucoup.

Lorsque vous entrez FragmentB à partir de FragmentA, l'instance FragmentA existante n'est pas détruite. Lorsque vous appuyez sur Retour dans FragmentB et que vous revenez à FragmentA, nous ne créons pas de nouvelle instance FragmentA. Les instances FragmentA existantes onCreateView()seront appelées.

L'essentiel est que nous ne devrions pas gonfler à nouveau la vue dans FragmentA onCreateView(), car nous utilisons l'instance de FragmentA existante. Nous devons enregistrer et réutiliser le rootView.

Le code suivant fonctionne bien. Il ne conserve pas seulement l'état des fragments, mais réduit également la charge de la RAM et du processeur (car nous ne gonflons que la mise en page si nécessaire). Je ne peux pas croire que l'exemple de code et de document de Google ne le mentionne jamais mais gonfle toujours la mise en page .

Version 1 (n'utilisez pas la version 1. Utilisez la version 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Mise à jour du 3 mai 2005: -------

Comme les commentaires l'ont mentionné, _rootView.getParent()est parfois nul dans onCreateView, ce qui provoque le plantage. La version 2 supprime _rootView dans onDestroyView (), comme dell116 l'a suggéré. Testé sur Android 4.0.3, 4.4.4, 5.1.0.

Version 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

AVERTISSEMENT!!!

C'est un HACK! Bien que je l'utilise dans mon application, vous devez tester et lire attentivement les commentaires.

Vince Yuan
la source
38
Tenir une référence à la racine du fragment entier est une mauvaise idée de l'OMI. Si vous ajoutez continuellement plusieurs fragments à une backstack et que tous contiennent sa vue racine (qui a une assez grande empreinte mémoire), alors il y a de fortes chances que vous vous retrouviez avec OutOfMemoryError puisque tous les fragments contiennent une référence rootview et GC ne peut pas le ramasser. Je pense que la meilleure approche est de gonfler la vue tout le temps (et de laisser le système Android gérer sa création / destruction de vue) et onActivityCreated / onViewCreated vérifie si vos données sont nulles. Si oui, chargez-le, sinon définissez les données sur vues.
traninho
15
Ne fais pas ça! Lorsque la hiérarchie de vues du fragment est créée, elle contient une référence interne à l'activité qui contenait le fragment à ce moment-là. Lorsqu'un changement de configuration se produit, l'activité est souvent recréée. La réutilisation de l'ancienne mise en page conserve cette activité zombie en mémoire avec les objets auxquels elle fait également référence. Une perte de mémoire comme celle-ci nuit aux performances et fait de votre application un candidat idéal pour une résiliation immédiate lorsqu'elle n'est pas au premier plan.
Krylez
4
@AllDayAmazing C'est un bon point. Pour être honnête, je suis très confus en ce moment. Quelqu'un peut-il essayer d'expliquer pourquoi le fait de tenir une référence à la vue racine d'un fragment n'est pas correct, mais de ne conserver une référence que pour tout enfant de rootview (qui a de toute façon une référence à rootview) est correct?
traninho
2
RESTEZ LOIN DE CELA sauf si vous voulez perdre 5 heures à découvrir ce qui dérange votre code ..... alors seulement pour découvrir que c'est la cause. Maintenant, je dois refactoriser un tas de trucs parce que j'ai utilisé ce hack. Il est préférable d'utiliser fragmentTransaction.add si vous voulez garder l'interface utilisateur du fragment intacte lorsque vous en mettez un autre en vue (même en haut). fragmentTransaction.replace () est destiné à détruire les vues du fragment ..... ne combattez pas le système.
dell116
2
@VinceYuan - J'ai testé avec la dernière bibliothèque v7-appcompat sur Android 5.1 et cela a laissé 6 instances d'un fragment qui aurait dû être supprimé dans le FragmentManager de mon activité. Même si le GC le gère correctement (ce que je ne crois pas), cela entraîne une charge inutile sur la mémoire de votre application ainsi que de l'appareil en général. Le simple fait d'utiliser .add () supprime complètement le besoin de tout ce code hacky. Faire cela va complètement à l'encontre de ce que l'utilisation de FragmentTransaction.replace () était censée faire en premier lieu.
dell116
53

Je suppose qu'il existe une autre façon de réaliser ce que vous recherchez. Je ne dis pas que c'est une solution complète, mais cela a servi le but dans mon cas.

Ce que j'ai fait, c'est au lieu de remplacer le fragment, je viens d'ajouter le fragment cible. Donc, fondamentalement, vous utiliserez la add()méthode à la place replace().

Qu'est-ce que j'ai fait d'autre. Je cache mon fragment actuel et je l'ajoute également à backstack.

Par conséquent, il chevauche le nouveau fragment sur le fragment actuel sans détruire sa vue (vérifier que sa onDestroyView()méthode n'est pas appelée. De plus, l'ajouter à backstateme donne l'avantage de reprendre le fragment.

Voici le code:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

Le système AFAIK n'appelle que onCreateView()si la vue est détruite ou non créée. Mais ici, nous avons enregistré la vue en ne la supprimant pas de la mémoire. Cela ne créera donc pas une nouvelle vue.

Et lorsque vous reviendrez du Fragment de destination, le dernier FragmentTransactionfragment supérieur supprimant apparaîtra, ce qui fera apparaître la vue la plus haute (SourceFragment) sur l'écran.

COMMENTAIRE: Comme je l'ai dit, ce n'est pas une solution complète car elle ne supprime pas la vue du fragment Source et occupe donc plus de mémoire que d'habitude. Mais encore, servir le but. De plus, nous utilisons un mécanisme totalement différent de masquage de la vue au lieu de la remplacer, ce qui n'est pas traditionnel.

Ce n'est donc pas vraiment pour la façon dont vous maintenez l'état, mais pour la façon dont vous maintenez la vue.

kaushal trivedi
la source
Dans mon cas, l'ajout d'un fragment au lieu de le remplacer pose des problèmes lors de l'utilisation d'interrogation ou de tout autre type de requête Web est utilisé dans le fragment. Je souhaite mettre en pause cette interrogation dans le fragment A lorsque le fragment B est ajouté. Une idée à ce sujet?
Uma
Comment utilisez-vous le sondage dans FirstFragment? Vous devez le faire manuellement car les deux fragments restent en mémoire.Vous pouvez donc utiliser leurs instances pour effectuer l'action nécessaire.C'est l'indice, générer un événement dans l'activité principale qui fait quelque chose lorsque vous ajoutez un deuxième fragment. J'espère que cela aidera.
kaushal trivedi
1
Merci pour l'indice =). J'ai fait cela. Mais est-ce la seule façon de le faire? Et une manière appropriée? Aussi, lorsque j'appuie sur le bouton d'accueil et relance l'application, tous les fragments redeviennent actifs. Supposons que je sois ici dans le fragment B de cette manière. Activity A{Fragment A --> Fragment B}lorsque je lance à nouveau l'application après avoir appuyé sur le bouton d'accueil, les deux fragments onResume()sont appelés et par conséquent, ils commencent leur interrogation. Comment puis-je contrôler cela?
Uma
1
Malheureusement, vous ne pouvez pas, le système ne fonctionne pas dans un comportement normal de cette manière, il considérera à la fois le fragment comme un enfant direct de l'activité.Bien que cela ait servi à maintenir l'état du fragment, d'autres choses normales deviennent très difficiles à gérer. découvert tous ces problèmes, maintenant ma suggestion est de ne pas aller dans cette direction.
kaushal trivedi
1
Bien sûr, en dernier lieu, je dirai de ne pas suivre cette approche tant que vous n'avez pas trouvé une autre solution, car c'est difficile à gérer.
kaushal trivedi
7

Je suggérerais une solution très simple.

Prenez la variable de référence View et définissez la vue dans OnCreateView. Vérifiez si la vue existe déjà dans cette variable, puis renvoyez la même vue.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }
Mandeep Singh
la source
1
Il y a un risque de fuite de mémoire si nous n'avons pas supprimé la variable 'fragmentView' dans onDestroy ()
Arun PM
@ArunPM alors comment supprimer FragmentView dans onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }est approprié pour effacer la mémoire?
Mehmet Gür le
1
@ MehmetGür J'utilise cette solution plusieurs fois. Jusqu'à présent, je n'ai eu aucune erreur de fuite de mémoire. Mais vous pouvez utiliser la solution ArunPM avec cela si vous le souhaitez. Je pense qu'il dit de définir fragmentView sur null dans la méthode OnDestroy ().
Mandeep Singh le
1
J'utilise LeakCanary pour détecter les fuites de mémoire et ses problèmes de fuite lorsque j'ai suivi cette méthode. Mais comme @Mandeep Sigh l'a mentionné dans le commentaire, nous pouvons surmonter ce problème en attribuant nullune fragmentView variable à la onDestroy()méthode.
Arun PM le
1
À ma connaissance, lorsqu'un fragment est détruit, la vue associée au fragment est effacée onDestroyView(). Cet effacement ne se produit pas pour notre variable de vue de sauvegarde (ici fragmentView ) et provoquera une fuite de mémoire lorsque le fragment sera empilé / détruit. Vous pouvez trouver la même référence dans [Causes courantes des fuites de mémoire] ( square.github.io/leakcanary/fundamentals/… ) dans l'introduction de LeakCanery.
Arun PM le
6

Je suis tombé sur ce problème dans un fragment contenant une carte, qui a trop de détails de configuration pour enregistrer / recharger. Ma solution était de garder ce Fragment actif tout le temps (similaire à ce que @kaushal a mentionné).

Supposons que vous ayez le Fragment A actuel et que vous souhaitiez afficher le Fragment B.Résumé des conséquences:

  • replace () - retirez le Fragment A et remplacez-le par le Fragment B.Le Fragment A sera recréé une fois ramené au premier plan
  • add () - (créer et) ajouter un fragment B et il chevauche le fragment A, qui est toujours actif en arrière-plan
  • remove () - peut être utilisé pour supprimer le fragment B et revenir à A. Le fragment B sera recréé lorsqu'il sera appelé plus tard

Par conséquent, si vous souhaitez conserver les deux fragments "enregistrés", activez-les simplement en utilisant hide () / show ().

Avantages : méthode facile et simple pour garder plusieurs fragments en cours d'exécution
Inconvénients : vous utilisez beaucoup plus de mémoire pour tous les faire fonctionner. Peut rencontrer des problèmes, par exemple l'affichage de nombreuses grandes images bitmap

Teo Inke
la source
pouvez-vous s'il vous plaît me dire quand nous supprimons le fragment b et revenons à A, quelle méthode est appelée dans le fragment A? je veux prendre des mesures lorsque nous supprimons le fragment B.
Google
5

onSaveInstanceState() n'est appelé que s'il y a un changement de configuration.

Depuis le passage d'un fragment à un autre, il n'y a pas de changement de configuration donc aucun appel à onSaveInstanceState()n'est là. Quel état n'est pas sauvé? Pouvez-vous préciser?

Si vous entrez du texte dans EditText, il sera enregistré automatiquement. Tout élément d'interface utilisateur sans identifiant est l'élément dont l'état d'affichage ne doit pas être enregistré.

utilisateur2779311
la source
onSaveInstanceState()est également appelé lorsque le système détruit Activity car il manque de ressources.
Marcel Bro
0

Ici, car onSaveInstanceStatein fragment n'appelle pas lorsque vous ajoutez fragment dans backstack. Le cycle de vie du fragment dans la backstack lors de la restauration commence onCreateViewet se termine onDestroyViewalors qu'il onSaveInstanceStateest appelé entre onDestroyViewet onDestroy. Ma solution est de créer une variable d'instance et d'initialiser onCreate. Exemple de code:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

Et enregistrez-le onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}
Ling Boo
la source
0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
Hardik Vasani
la source
0

Mon problème était similaire mais je me suis vaincu sans garder le fragment vivant. Supposons que vous ayez une activité qui a 2 fragments - F1 et F2. F1 est démarré initialement et disons qu'il contient des informations sur l'utilisateur, puis, à certaines conditions, F2 apparaît en demandant à l'utilisateur de remplir un attribut supplémentaire - son numéro de téléphone. Ensuite, vous voulez que ce numéro de téléphone revienne à F1 et termine l'inscription, mais vous réalisez que toutes les informations sur les utilisateurs précédents sont perdues et que vous ne disposez pas de leurs données précédentes. Le fragment est recréé à partir de zéro et même si vous avez enregistré ces informations dans onSaveInstanceStatele bundle, il revient nul dans onActivityCreated.

Solution: enregistrez les informations requises en tant que variable d'instance dans l'activité d'appel. Passez ensuite cette variable d'instance dans votre fragment.

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

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

Donc, suite à mon exemple: avant d'afficher F2, j'enregistre les données utilisateur dans la variable d'instance à l'aide d'un rappel. Ensuite, je lance F2, l'utilisateur remplit le numéro de téléphone et appuie sur Enregistrer. J'utilise un autre rappel en activité, collecte ces informations et remplace mon fragment F1, cette fois il contient des données groupées que je peux utiliser.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Plus d'informations sur les callbacks peuvent être trouvées ici: https://developer.android.com/training/basics/fragments/communicating.html

Dodi
la source
0

première : utilisez simplement la méthode add au lieu de la méthode replace de la classe FragmentTransaction, puis vous devez ajouter secondFragment à la pile par la méthode addToBackStack

seconde : au retour, vous devez appeler popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};
Milad Ahmadi
la source
0

Remplacez un fragment à l'aide du code suivant:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

L'activité onBackPressed () est:

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}
Pratibha Sarode
la source
0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);
Hitesh Manwani
la source
1
Merci pour cet extrait de code, qui pourrait fournir une aide limitée et immédiate. Une explication appropriée améliorerait considérablement sa valeur à long terme en montrant pourquoi c'est une bonne solution au problème, et la rendrait plus utile aux futurs lecteurs avec d'autres questions similaires. Veuillez modifier votre réponse pour ajouter des explications, y compris les hypothèses que vous avez formulées.
Machavity
0

Solution parfaite qui trouve l'ancien fragment dans la pile et le charge s'il existe dans la pile.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

utilisez-le comme

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Khemraj
la source