Comment reprendre le Fragment de BackStack s'il existe

J'apprends à utiliser des fragments. J'ai trois instances de Fragment qui sont initialisées en haut de la classe. J'ajoute le fragment à une activité comme celle-ci:

Déclarer et initialiser:

Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();

Remplacement/Ajout:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();

Ces extraits fonctionnent correctement. Chaque fragment est attaché à l'activité et est enregistré dans la pile arrière sans aucun problème.

, Donc quand je lance A, C, et puis B, la pile ressemble ce:

| |
|B|
|C|
|A|
___

Et lorsque j'appuie sur le bouton 'retour', B est détruit et C est repris.

Mais, quand je lance fragment A une deuxième fois, au lieu de reprendre de la pile arrière, il est ajouté en haut de la pile arrière

| |
|A|
|C|
|A|
___

Mais je veux reprendre A et détruire tous les fragments dessus (le cas échéant). En fait, j'aime juste le comportement par défaut de la pile arrière.

Comment puis-je accomplir cela?

Attendu: (A devrait être repris et en haut les fragments doivent être détruits)

| |
| |
| |
|A|
___

Edit: (suggéré par Un--C)

C'est mon code d'essai:

private void selectItem(int position) {
        Fragment problemSearch = null, problemStatistics = null;
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        String backStateName = null;
        Fragment fragmentName = null;
        boolean fragmentPopped = false;
        switch (position) {
        case 0:
            fragmentName = profile;
            break;
        case 1:
            fragmentName = submissionStatistics;
            break;
        case 2:
            fragmentName = solvedProblemLevel;
            break;
        case 3:
            fragmentName = latestSubmissions;
            break;
        case 4:
            fragmentName = CPExercise;
            break;
        case 5:
            Bundle bundle = new Bundle();
            bundle.putInt("problem_no", problemNo);
            problemSearch = new ProblemWebView();
            problemSearch.setArguments(bundle);
            fragmentName = problemSearch;
            break;
        case 6:
            fragmentName = rankList;
            break;
        case 7:
            fragmentName = liveSubmissions;
            break;
        case 8:
            Bundle bundles = new Bundle();
            bundles.putInt("problem_no", problemNo);
            problemStatistics = new ProblemStatistics();
            problemStatistics.setArguments(bundles);
            fragmentName = problemStatistics;
        default:
            break;
        }
        backStateName = fragmentName.getClass().getName();
        fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
        if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();

        // I am using drawer layout
        mDrawerList.setItemChecked(position, true);
        setTitle(title[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

Le problème est que quand je lance A, puis B, puis appuyez sur la touche 'retour', B est supprimé et A est repris. et en appuyant sur "retour" une deuxième fois devrait quitter l'application. Mais il montre une fenêtre vide et je dois appuyer une troisième fois pour la fermer.

Aussi, lorsque je lance A, puis B, puis C, puis B Encore une fois...

Attendu:

| |
| |
|B|
|A|
___

Effectif:

| |
|B|
|B|
|A|
___

Devrais-je remplacer onBackPressed() avec une personnalisation ou est-ce que je manque quelque chose?

114
demandé sur Braden Best 2013-08-19 07:20:56

4 réponses

En lisant la documentation , Il existe un moyen de faire apparaître la pile arrière en fonction du nom de la transaction ou de l'id fourni par commit. Utiliser le nom peut être plus facile car il ne devrait pas nécessiter de garder une trace d'un nombre qui peut changer et renforce la logique "entrée de pile arrière unique".

Puisque vous ne voulez qu'une seule entrée de pile arrière par Fragment, Faites en sorte que l'état arrière nomme le nom de classe du Fragment (via getClass().getName()). Ensuite, lors du remplacement d'un Fragment, utilisez le popBackStackImmediate() méthode. S'il renvoie true, cela signifie qu'il y a une instance du Fragment dans la pile arrière. Sinon, exécutez réellement la logique de remplacement de Fragment.

private void replaceFragment (Fragment fragment){
  String backStateName = fragment.getClass().getName();

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment);
    ft.addToBackStack(backStateName);
    ft.commit();
  }
}

Modifier

Le problème est - quand je lance A, Puis B, puis appuyez sur le bouton Retour, B est supprimé et Une reprise. et en appuyant à nouveau sur le bouton retour devrait quitter l'application. Mais il montre une fenêtre vide et besoin d'une autre presse pour le fermer.

C'est parce que le FragmentTransaction est ajouté à la pile arrière pour nous assurer que nous pouvons pop les fragments sur le dessus plus tard. Une solution rapide pour cela est de remplacer onBackPressed() et de terminer l'activité si la pile arrière ne contient que 1 Fragment

@Override
public void onBackPressed(){
  if (getSupportFragmentManager().getBackStackEntryCount() == 1){
    finish();
  }
  else {
    super.onBackPressed();
  }
}

En ce qui concerne les entrées de pile arrière en double, votre instruction conditionnelle qui remplace le fragment s'il n'a pas été sauté est clairement différent de ce que mon extrait de code original. ce que vous faites est d'ajouter à la pile arrière, que la pile arrière soit ou non sauté.

Quelque chose comme ça devrait être plus proche de ce que vous voulez:

private void replaceFragment (Fragment fragment){
  String backStateName =  fragment.getClass().getName();
  String fragmentTag = backStateName;

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment, fragmentTag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
  } 
}

Le conditionnel a été légèrement modifié car la sélection du même fragment alors qu'il était visible a également provoqué des entrées en double.

Mise en œuvre:

Je suggère fortement de ne pas séparer la méthode replaceFragment() Mise à jour comme vous l'avez fait dans votre code. Toute la logique est contenue dans cette méthode et les pièces mobiles peuvent causer des problèmes.

Cela signifie que vous devez copier la mise à jour replaceFragment() méthode dans votre classe puis changer

backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();

Donc c'est simplement

replaceFragment (fragmentName);

MODIFIER #2

Pour mettre à jour le tiroir lorsque la pile arrière change, créez une méthode qui accepte dans un Fragment et compare les noms de classe. Si quelque chose correspond, modifiez le titre et la sélection. Ajoutez également un OnBackStackChangedListener et demandez-lui d'appeler votre méthode de mise à jour s'il y a un Fragment valide.

Par exemple, dans onCreate() de L'activité, ajoutez

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {

  @Override
  public void onBackStackChanged() {
    Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
    if (f != null){
      updateTitleAndDrawer (f);
    }

  }
});

Et l'autre méthode:

private void updateTitleAndDrawer (Fragment fragment){
  String fragClassName = fragment.getClass().getName();

  if (fragClassName.equals(A.class.getName())){
    setTitle ("A");
    //set selected item position, etc
  }
  else if (fragClassName.equals(B.class.getName())){
    setTitle ("B");
    //set selected item position, etc
  }
  else if (fragClassName.equals(C.class.getName())){
    setTitle ("C");
    //set selected item position, etc
  }
}

Maintenant, chaque fois que la pile arrière change, le titre et la position cochée refléteront le visible Fragment.

240
répondu A--C 2013-08-19 16:22:33

Je pense que cette méthode mon résoudre votre problème:

public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) {


    FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
    manager.findFragmentByTag ( tag );
    FragmentTransaction ft = manager.beginTransaction ();

    if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag..
        ft.add ( fragmentHolderLayoutId, fragment, tag );
        ft.addToBackStack ( tag );
        ft.commit ();
    }
    else {
        ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
    }
}

Qui a été initialement affiché dans Cette Question

4
répondu erluxman 2017-05-23 11:55:02

Étape 1: Implémenter une interface avec votre classe d'activité

public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        .............
        FragmentManager fragmentManager = getFragmentManager();           
        fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit();
    }

    private void switchFragment(Fragment fragment){            
      FragmentManager fragmentManager = getFragmentManager();
      fragmentManager.beginTransaction()
        .replace(R.id.frame_container, fragment).addToBackStack("Tag").commit();
    }

    @Override
    public void onBackStackChanged() {
    FragmentManager fragmentManager = getFragmentManager();

    System.out.println("@Class: SummaryUser : onBackStackChanged " 
            + fragmentManager.getBackStackEntryCount());

    int count = fragmentManager.getBackStackEntryCount();

    // when a fragment come from another the status will be zero
    if(count == 0){

        System.out.println("again loading user data");

        // reload the page if user saved the profile data

        if(!objPublicDelegate.checkNetworkStatus()){

            objPublicDelegate.showAlertDialog("Warning"
                    , "Please check your internet connection");

        }else {

            objLoadingDialog.show("Refreshing data..."); 

            mNetworkMaster.runUserSummaryAsync();
        }

        // IMPORTANT: remove the current fragment from stack to avoid new instance
        fragmentManager.removeOnBackStackChangedListener(this);

    }// end if
   }       
}

Étape 2: Lorsque vous appelez un autre fragment ajouter cette méthode:

String backStateName = this.getClass().getName();

FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(this); 

Fragment fragmentGraph = new GraphFragment();
Bundle bundle = new Bundle();
bundle.putString("graphTag",  view.getTag().toString());
fragmentGraph.setArguments(bundle);

fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragmentGraph)
.addToBackStack(backStateName)
.commit();
3
répondu Vinod Joshi 2015-08-06 18:34:19
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {

    @Override
    public void onBackStackChanged() {

        if(getFragmentManager().getBackStackEntryCount()==0) {
            onResume();
        }    
    }
});
0
répondu gushedaoren 2015-12-04 08:13:23