Problèmes avec la pile arrière de Fragment Android

J'ai un énorme problème avec la façon dont le back-up de fragment Android semble fonctionner et serait très reconnaissant pour toute aide offerte.

Imaginez que vous avez 3 fragments

  • Fonctionnalités de langue Java 7 avec Android
  • Comment obtenir du texte d'entrée à partir du clavier doux
  • Android appelant des fonctions JavaScript dans WebView
  • Traitement de pluriel Android de "zéro"
  • Quel type de bibliothèque Java OCR dois-je utiliser dans Android?
  • PendingIntent non ouvert Activité dans Android 4.3
  • [1] [2] [3]

    Je veux que l'utilisateur puisse naviguer [1] > [2] > [3] mais en revenant en arrière (en appuyant sur le bouton arrière) [3] > [1] .

    Comme je l'aurais imaginé, cela serait accompli en n'appelant pas addToBackStack(..) lors de la création de la transaction qui apporte le fragment [2] dans le support de fragment défini dans XML.

    La réalité de ceci semble être que si je ne veux pas [2] apparaître à nouveau lorsque l'utilisateur appuie sur le bouton arrière sur [3] , je ne dois pas appeler addToBackStack dans la transaction qui montre le fragment [3] . Cela semble complètement contre-intuitif (peut-être venant du monde iOS).

    Quoi qu'il en soit, si je le fais de cette façon, quand je vais de [1] > [2] et appuyez de nouveau vers l'arrière, je parviens à [1] comme prévu.

    Si je vais [1] > [2] > [3] , puis appuyez de nouveau pour revenir à [1] (comme prévu). Maintenant, le comportement étrange se produit quand j'essaie de sauter à [2] nouveau [1] . Tout d'abord [3] est brièvement affiché avant [2] . Si j'appuie sur ce point [3] s'affiche, et si j'appuie une fois de plus, l'application sort.

    Quelqu'un peut-il m'aider à comprendre ce qui se passe ici?

    Et voici le fichier xml de mise en page pour mon activité principale:

     <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <fragment android:id="@+id/headerFragment" android:layout_width="match_parent" android:layout_height="wrap_content" class="com.fragment_test.FragmentControls" > <!-- Preview: layout=@layout/details --> </fragment> <FrameLayout android:id="@+id/detailFragment" android:layout_width="match_parent" android:layout_height="fill_parent" /> 

    Mise à jour C'est le code que j'utilise pour créer par nav heirarchy

      Fragment frag; FragmentTransaction transaction; //Create The first fragment [1], add it to the view, BUT Dont add the transaction to the backstack frag = new Fragment1(); transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.detailFragment, frag); transaction.commit(); //Create the second [2] fragment, add it to the view and add the transaction that replaces the first fragment to the backstack frag = new Fragment2(); transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.detailFragment, frag); transaction.addToBackStack(null); transaction.commit(); //Create third fragment, Dont add this transaction to the backstack, because we dont want to go back to [2] frag = new Fragment3(); transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.detailFragment, frag); transaction.commit(); //END OF SETUP CODE------------------------- //NOW: //Press back once and then issue the following code: frag = new Fragment2(); transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.detailFragment, frag); transaction.addToBackStack(null); transaction.commit(); //Now press back again and you end up at fragment [3] not [1] 

    Merci beaucoup

  • Comment obtenir des vacances nationales du pays sélectionné
  • Définir Valeur à Enum - Java
  • Comment annuler la liste de cryptage envoyée au serveur par Android lors de l'utilisation de HttpsURLConnection?
  • Déterminer périphérique ip publicitaire
  • Ajouter un filtre de recherche sur RecyclerView avec des cartes?
  • Erreur - L'importation com.google ne peut pas être résolue
  • 8 Solutions collect form web for “Problèmes avec la pile arrière de Fragment Android”

    Explication: sur ce qui se passe ici?

    Si nous gardons à l'esprit que .replace() est égal à .remove().add() que nous connaissons par la documentation:

    Remplacez un fragment existant qui a été ajouté à un conteneur. C'est essentiellement le même que d'appeler remove(Fragment) pour tous les fragments actuellement ajoutés qui ont été ajoutés avec le même containerViewId , puis add(int, Fragment, String) avec les mêmes arguments ici.

    Alors, ce qui se passe est comme ça (j'ajoute des chiffres au frag pour le rendre plus clair):

     // transaction.replace(R.id.detailFragment, frag1); Transaction.remove(null).add(frag1) // frag1 on view // transaction.replace(R.id.detailFragment, frag2).addToBackStack(null); Transaction.remove(frag1).add(frag2).addToBackStack(null) // frag2 on view // transaction.replace(R.id.detailFragment, frag3); Transaction.remove(frag2).add(frag3) // frag3 on view 

    (Ici, toutes les choses trompeuses commencent à se produire)

    Rappelez-vous que .addToBackStack() enregistre uniquement la transaction et non le fragment comme lui-même! Alors maintenant, nous avons frag3 sur la mise en page:

     < press back button > // System pops the back stack and find the following saved back entry to be reversed: // [Transaction.remove(frag1).add(frag2)] // so the system makes that transaction backward!!! // tries to remove frag2 (is not there, so it ignores) and re-add(frag1) // make notice that system doesn't realise that there's a frag3 and does nothing with it // so it still there attached to view Transaction.remove(null).add(frag1) //frag1, frag3 on view (OVERLAPPING) // transaction.replace(R.id.detailFragment, frag2).addToBackStack(null); Transaction.remove(frag3).add(frag2).addToBackStack(null) //frag2 on view < press back button > // system makes saved transaction backward Transaction.remove(frag2).add(frag3) //frag3 on view < press back button > // no more entries in BackStack < app exits > 

    Solution possible

    Envisager la mise en œuvre de FragmentManager.BackStackChangedListener pour surveiller les changements dans la pile arrière et appliquer votre logique dans la méthode onBackStackChanged() :

    • Tracez un compte de transaction;
    • Vérifiez une transaction particulière par nom FragmentTransaction.addToBackStack(String name);
    • Etc.

    Droite!!! Après beaucoup de traction de cheveux, j'ai finalement travaillé à faire fonctionner correctement.

    Il semble que le fragment [3] ne soit pas retiré de la vue lorsque le dos est pressé, donc vous devez le faire manuellement!

    Tout d'abord, n'utilisez pas replace (), mais utilisez plutôt supprimer et ajouter séparément. Il semble que le remplacement () ne fonctionne pas correctement.

    La partie suivante consiste à remplacer la méthode onKeyDown et à supprimer le fragment actuel chaque fois que vous appuyez sur le bouton Précédent.

     @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (getSupportFragmentManager().getBackStackEntryCount() == 0) { this.finish(); return false; } else { getSupportFragmentManager().popBackStack(); removeCurrentFragment(); return false; } } return super.onKeyDown(keyCode, event); } public void removeCurrentFragment() { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); Fragment currentFrag = getSupportFragmentManager().findFragmentById(R.id.detailFragment); String fragName = "NONE"; if (currentFrag!=null) fragName = currentFrag.getClass().getSimpleName(); if (currentFrag != null) transaction.remove(currentFrag); transaction.commit(); } 

    J'espère que cela t'aides!

    Tout d'abord, merci @Arvis pour une explication d'ouverture des yeux.

    Je préfère une solution différente à la réponse acceptée ici pour ce problème. Je n'aime pas déranger avec un comportement de retour supérieur absolument absolument nécessaire et quand j'ai essayé d'ajouter et de supprimer des fragments de mon propre chef sans retour par défaut de la pile arrière lorsque le bouton de retour est pressé, je me suis retrouvé dans l'enfer de fragments 🙂 Si vous. Ajoutez f2 sur f1 lorsque vous l'enlevez. F1 n'appellera aucune méthode de rappel comme onResume, OnStart etc. et cela peut être très regrettable.

    Quoi qu'il en soit, je le fais comme suit:

    Actuellement affiché n'est que le fragment f1.

    F1 -> f2

     Fragment2 f2 = new Fragment2(); this.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content,f2).addToBackStack(null).commit(); 

    Rien d'extraordinaire ici. Que dans le fragment f2, ce code vous emmène au fragment f3.

    F2 -> f3

     Fragment3 f3 = new Fragment3(); getActivity().getSupportFragmentManager().popBackStack(); getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit(); 

    Je ne suis pas sûr de lire les documents si cela devrait fonctionner, cette méthode de transaction poping est dite asynchrone, et peut-être une meilleure façon serait d'appeler popBackStackImmediate (). Mais jusqu'à présent, je peux dire sur mes appareils qu'il fonctionne parfaitement.

    Cette alternative serait:

     final FragmentActivity activity = getActivity(); activity.getSupportFragmentManager().popBackStackImmediate(); activity.getSupportFragmentManager().beginTransaction().replace(R.id.main_content, f3).addToBackStack(null).commit(); 

    Ici, il y aura un bref retour à f1 beofre en passant à f3, donc un léger problème là-bas.

    C'est en fait tout ce que vous avez à faire, pas besoin de remplacer le comportement de la pile de retour …

    Je sais que c'est un vieux repli, mais j'ai eu le même problème et je le répars comme ça:

    D'abord, Ajoutez Fragment1 à BackStack avec un nom (par ex. "Frag1"):

     frag = new Fragment1(); transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.detailFragment, frag); transaction.addToBackStack("Frag1"); transaction.commit(); 

    Et puis, chaque fois que vous voulez revenir à Fragment1 (même après avoir ajouté 10 fragments au-dessus), appelez simplement popBackStackImmediate avec le nom:

     getSupportFragmentManager().popBackStackImmediate("Frag1", 0); 

    J'espère que ça aidera quelqu'un 🙂

    Après @Arvis réponse, j'ai décidé de creuser encore plus profondément et j'ai écrit un article technique à ce sujet ici: http://www.andreabaccega.com/blog/2015/08/16/how-to-avoid-fragments-overlapping- Debout-back-stack-nightmare-in-android /

    Pour les développeurs paresseux autour. Ma solution consiste à ajouter toujours les transactions au backstack et à exécuter un FragmentManager.popBackStackImmediate() supplémentaire.popBackStackImmediate FragmentManager.popBackStackImmediate() lorsque nécessaire (automatiquement).

    Le code est très peu de lignes de code et, dans mon exemple, je voulais passer de C à A sans retourner à "B" si l'utilisateur ne s'est pas approfondi dans le backstack (ex de C navigue vers D).

    Par conséquent, le code attaché fonctionnerait comme suit A -> B -> C (arrière) -> A & A -> B -> C -> D (arrière) -> C (arrière) -> B (arrière) -> A

     fm.beginTransaction().replace(R.id.content, new CFragment()).commit() 

    Ont été délivrés de "B" à "C" comme dans la question.

    Ok, ok, voici le code 🙂

     public static void performNoBackStackTransaction(FragmentManager fragmentManager, String tag, Fragment fragment) { final int newBackStackLength = fragmentManager.getBackStackEntryCount() +1; fragmentManager.beginTransaction() .replace(R.id.content, fragment, tag) .addToBackStack(tag) .commit(); fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { int nowCount = fragmentManager.getBackStackEntryCount(); if (newBackStackLength != nowCount) { // we don't really care if going back or forward. we already performed the logic here. fragmentManager.removeOnBackStackChangedListener(this); if ( newBackStackLength > nowCount ) { // user pressed back fragmentManager.popBackStackImmediate(); } } } }); } 

    Si vous tapez avec addToBackStack () et popBackStack (), utilisez simplement

     FragmentTransaction ft =getSupportFragmentManager().beginTransaction(); ft.replace(R.id.content_frame, new HomeFragment(), "Home"); ft.commit();` 

    Dans votre Activité dans OnBackPressed (), découvrez le fargment par tag, puis faites vos affaires

     Fragment home = getSupportFragmentManager().findFragmentByTag("Home"); if (home instanceof HomeFragment && home.isVisible()) { // do you stuff } 

    Pour plus d'informations https://github.com/DattaHujare/NavigationDrawer, je n'utilise jamais addToBackStack () pour gérer le fragment.

    Je pense, quand j'ai lu votre histoire, [3] est également sur la plate-forme arrière. Cela explique pourquoi vous le voyez clignotant.

    La solution serait de ne jamais définir [3] sur la pile.

    J'ai eu un problème similaire où j'ai eu 3 fragments consécutifs dans la même Activity [M1.F0] -> [M1.F1] -> [M1.F2] suivi d'un appel à une nouvelle Activity [M2]. Si l'utilisateur a appuyé sur un bouton dans [M2], je voulais revenir à [M1, F1] au lieu de [M1, F2] ce qui a déjà fait le comportement de la contre-pression.

    Pour ce faire, je supprime [M1, F2], l'émission d'appel sur [M1, F1], engagez la transaction, puis ajoutez [M1, F2] en l'appelant avec cache. Cela a supprimé la pression arrière supplémentaire qui aurait été autrement laissée derrière.

     // Remove [M1.F2] to avoid having an extra entry on back press when returning from M2 final FragmentTransaction ftA = fm.beginTransaction(); ftA.remove(M1F2Fragment); ftA.show(M1F1Fragment); ftA.commit(); final FragmentTransaction ftB = fm.beginTransaction(); ftB.hide(M1F2Fragment); ftB.commit(); 

    Bonjour Après avoir fait ce code: je ne peux pas voir la valeur de Fragment2 en appuyant sur la touche Retour. Mon code:

     FragmentTransaction ft = fm.beginTransaction(); ft.add(R.id.frame, f1); ft.remove(f1); ft.add(R.id.frame, f2); ft.addToBackStack(null); ft.remove(f2); ft.add(R.id.frame, f3); ft.commit(); @Override public boolean onKeyDown(int keyCode, KeyEvent event){ if(keyCode == KeyEvent.KEYCODE_BACK){ Fragment currentFrag = getFragmentManager().findFragmentById(R.id.frame); FragmentTransaction transaction = getFragmentManager().beginTransaction(); if(currentFrag != null){ String name = currentFrag.getClass().getName(); } if(getFragmentManager().getBackStackEntryCount() == 0){ } else{ getFragmentManager().popBackStack(); removeCurrentFragment(); } } return super.onKeyDown(keyCode, event); } public void removeCurrentFragment() { FragmentTransaction transaction = getFragmentManager().beginTransaction(); Fragment currentFrag = getFragmentManager().findFragmentById(R.id.frame); if(currentFrag != null){ transaction.remove(currentFrag); } transaction.commit(); } 
    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.