Fragment onResume () & onPause () n'est pas appelé sur backstack

195

J'ai plusieurs fragments à l'intérieur d'une activité. Sur un bouton, cliquez sur Je commence un nouveau fragment, en l'ajoutant à backstack. Je m'attendais naturellement à ce que la onPause()méthode du Fragment actuel et onResume()du nouveau Fragment soit appelée. Eh bien, cela ne se produit pas.

LoginFragment.java

public class LoginFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
      final FragmentManager mFragmentmanager =  getFragmentManager();

      Button btnHome  = (Button)view.findViewById(R.id.home_btn);
      btnHome.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view){
           HomeFragment fragment    = new HomeFragment();
           FragmentTransaction ft2   =  mFragmentmanager.beginTransaction();
           ft2.setCustomAnimations(R.anim.slide_right, R.anim.slide_out_left
                    , R.anim.slide_left, R.anim.slide_out_right);
           ft2.replace(R.id.middle_fragment, fragment);
           ft2.addToBackStack(""); 
           ft2.commit();    
         }
      });
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of LoginFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
    Log.e("DEBUG", "OnPause of loginFragment");
    super.onPause();
  }
}

HomeFragment.java

public class HomeFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of HomeFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
     Log.e("DEBUG", "OnPause of HomeFragment");
     super.onPause();
  }
}

Ce à quoi je m'attendais, c'était,

  1. Lorsque le bouton est cliqué, LoginFragment sera remplacé par HomeFragment , onPause()de LoginFragment et onResume()de HomeFragment est appelé
  2. Lorsque back est pressé, HomeFragment est sorti et LoginFragment est vu, et onPause()de HomeFragment et onResume()de LoginFragment est appelé.

Ce que je reçois est,

  1. Lorsque le bouton est cliqué, HomeFragment remplace correctement LoginFragment , onResume () de HomeFragment est appelé, mais onPause () de LoginFragment n'est jamais appelé.
  2. Lorsque vous appuyez à nouveau, HomeFragment apparaît correctement pour révéler LoginFragment , onPause () de HomeFragment est appelé, mais onResume () de LoginFragment n'est jamais appelé.

Est-ce le comportement normal? Pourquoi onResume()de LoginFragment pas appelé quand j'appuie sur le bouton de retour.

Krishnabhadra
la source
Ajoutez le code d'activité qui gère les fragments.
blessenm
J'ai un problème d'échantillon, en pause, ne vous appelez pas, comment avez-vous résolu cela,
Sam
J'ai eu le même problème mais j'ai réalisé que j'utilisais ft2.add (); au lieu de ft2.replace (). La seule autre raison serait que votre activité garde une référence au fragment (l'ajoute à une collection ou l'affecte à une variable de classe)
Friggles
3
J'ai le même problème. J'ai remarqué que .replace () appellera les méthodes de cycle de vie nécessaires, mais il détruit essentiellement le fragment. En outre, onSaveInstanceState n'est pas appelé. En tant que tel, je ne peux pas garder son état. Donc, je dois utiliser add, mais onResume / Pause ne s'appelle pas :(
ariets
FWIW, mon expérience est que les fragments de bibliothèque de support appellent onPause et onResume lors du push / popping backstack, mais pas les fragments intégrés Android. Je n'ai pas encore trouvé de solution de contournement appropriée pour cela.
benkc

Réponses:

185

Les fragments onResume()ou onPause()seront appelés que lorsque les activités onResume()ou onPause()est appelé. Ils sont étroitement couplés au Activity.

Lisez la section Gestion du cycle de vie des fragments de cet article .

Sagar Waghmare
la source
1
Dans l'article, il est dit que "une fois que l'activité a atteint son état de reprise, vous pouvez librement ajouter et supprimer des fragments à l'activité. Ainsi, ce n'est que lorsque l'activité est à l'état de reprise que le cycle de vie d'un fragment peut changer indépendamment". Est-ce à dire que le fragment onResume peut être appelé même si l'activité onResume n'est pas appelée?
v4r
3
comme pour les fragments natifs (non pris en charge) dans 4.4 (pas sûr que ce soit vrai pour les anciennes versions) onPause () et onResume () sont appelés non seulement lorsque ces événements se produisent dans l'activité, mais par exemple lorsque vous appelez replace () ou ajoutez () / remove () lors de l'exécution de la transaction, donc cette réponse est trompeuse au moins pour les versions récentes d'Android.
Dmide
19
Selon ce document, le fragment devrait en fait être déplacé à l'état arrêté lors de son échange dans le backstack. Mais non seulement onPause et onResume ne sont pas appelés, ni onStop et onStart - ni d'ailleurs aucune autre méthode de cycle de vie. Le guide est donc certainement trompeur.
benkc
1
Bien que cela ne soit pas lié, j'ai trouvé cette question en cherchant mon problème à onPause()être appelé après onSaveInstanceState()plutôt qu'avant. Cela peut être reproduit si vous vers un autre enfant dans mon FragmentStatePagerAdapter(disons que vous passez de l'enfant 0 à l'enfant 2, notez-le, cela se produit parce que l'enfant 0 est détruit lorsque l'enfant 2 s'ouvre )
Sufian
Pas sûr de ce que vous voulez dire. L'appel de ft.replace devrait déclencher onPause (du fragment remplacé) et onResume (du fragment de remplacement). Cela se fait indépendamment de toute activité ...
David Refaeli
20
  • Depuis que vous l'avez utilisé ft2.replace(), la FragmentTransaction.remove() méthode est appelée et Loginfragmentsera supprimée. Référez-vous à ceci . Donc, onStop()de LoginFragmentsera appelé à la place de onPause(). (Comme le nouveau fragment remplace complètement l'ancien).
  • Mais comme vous l'avez également utilisé ft2.addtobackstack(), l'état du Loginfragmentsera enregistré sous forme de bundle et lorsque vous cliquez sur le bouton de retour de HomeFragment, onViewStateRestored()sera appelé suivi onStart()de LoginFragment. Donc, finalement onResume(), ne sera pas appelé.
Gopal
la source
1
onViewStateRestoredest appelé si vous avezsetRetainInstance(true)
Farid
14

Voici ma version la plus robuste de la réponse de Gor (l'utilisation de fragments.size () n'est pas fiable car la taille n'est pas décrémentée après l'éclatement du fragment)

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager() != null) {

                Fragment topFrag = NavigationHelper.getCurrentTopFragment(getFragmentManager());

                if (topFrag != null) {
                    if (topFrag instanceof YourFragment) {
                        //This fragment is being shown. 
                    } else {
                        //Navigating away from this fragment. 
                    }
                }
            }
        }
    });

Et la méthode «getCurrentTopFragment»:

public static Fragment getCurrentTopFragment(FragmentManager fm) {
    int stackCount = fm.getBackStackEntryCount();

    if (stackCount > 0) {
        FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(stackCount-1);
        return  fm.findFragmentByTag(backEntry.getName());
    } else {
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null && fragments.size()>0) {
            for (Fragment f: fragments) {
                if (f != null && !f.isHidden()) {
                    return f;
                }
            }
        }
    }
    return null;
}
aaronmarino
la source
9

Si vous voulez vraiment remplacer un fragment à l'intérieur d'un autre fragment, vous devez utiliser des fragments imbriqués .

Dans votre code, vous devez remplacer

final FragmentManager mFragmentmanager =  getFragmentManager();

avec

final FragmentManager mFragmentmanager =  getChildFragmentManager();
Bersh
la source
5
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> fragments = getFragmentManager().getFragments();
            if (fragments.size() > 0 && fragments.get(fragments.size() - 1) instanceof YoureFragment){
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }

        }
    });

mais attention si plus d'un fragment est visible

Gor
la source
Merci, cela fonctionne, mais si un seul fragment est visible (donc, ViewPageravec des fragments fera référence à ses fragments).
CoolMind
3

J'ai un code très similaire au vôtre et s'il fonctionne avec onPause () et onResume (). Lors du changement de fragment, ces fonctions sont respectivement activées.

Code en fragment:

 @Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, proximidad, SensorManager.SENSOR_DELAY_NORMAL);
    sensorManager.registerListener(this, brillo, SensorManager.SENSOR_DELAY_NORMAL);
    Log.e("Frontales","resume");
}

@Override
public void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
    Log.e("Frontales","Pause");

}

Journal lors du changement de fragment:

05-19 22:28:54.284 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:28:57.002 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:28:58.697 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:00.840 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:29:02.248 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:03.718 2371-2371/madi.cajaherramientas E/Frontales: Pause

Fragment onCreateView:

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

    rootView = inflater.inflate(R.layout.activity_proximidad, container, false);
    ButterKnife.bind(this,rootView);
    inflar();
    setTextos();
    return rootView;
}

Action lorsque je recule (dans l'activité où je charge le fragment):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();

    } else {
        getFragmentManager().popBackStack();
    }

 }
PepitoGrillo
la source
2

Ce que je fais dans fragment enfant:

@Override
public void onDetach() {
   super.onDetach();
   ParentFragment pf = (ParentFragment) this.getParentFragment();
   pf.onResume();
}

Et puis remplacer onResume sur ParentFragment

Guillermo De Juan
la source
1
Vous ne devez jamais appeler les méthodes de cycle de vie manuellement, en particulier les unes à l'intérieur des autres
breakline
@breakline cette technique fonctionne. Avez-vous un autre moyen?
Vikash Parajuli
Oui, vous devez ajouter votre propre implémentation à appeler car les méthodes de cycle de vie sont également appelées par le système et si vous appelez les méthodes de cycle de vie les unes dans les autres de cette manière, vous risquez (et très probablement) de provoquer des problèmes ultérieurs.
breakline
1

Vous simple ne pouvez pas ajouter un fragment à un fragment. Cela doit se produire dans FragmentActivity. Je suppose que vous créez le LoginFragment dans une FragmentActivity, donc pour que cela fonctionne, vous devez ajouter le HomeFragment via le FragmentActivity lorsque la connexion se ferme.

Le point général est que vous avez besoin d'une classe FragmentActivity à partir de laquelle vous ajoutez chaque fragment au FragmentManager. Il n'est pas possible de le faire dans une classe Fragment.

Nickolaus
la source
Oui, ils sont à l'intérieur d'une activité fragmentaire.
Krishnabhadra
si des fragments sont ajoutés dynamiquement, vous pouvez ajouter autant de fragments que vous le souhaitez à un fragment mais pas à ceux définis dans la balise xml <fragment>
Illegal Argument
1

Si vous ajoutez le fragment en XML, vous ne pouvez pas les échanger dynamiquement. Ce qui se passe, c'est qu'ils sont trop, donc les événements ne se déclenchent pas comme on pourrait s'y attendre. Le problème est documenté dans cette question.FragmenManager remplace la superposition

Transformez middle_fragment en FrameLayout et chargez-le comme ci-dessous et vos événements se déclencheront.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();
Dustin
la source
1

Vous pouvez essayer ça,

Étape 1: remplacer la méthode sélectionnée par tabulation dans votre activité

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    try {
    if(MyEventsFragment!=null && tab.getPosition()==3)
    {
        MyEvents.fragmentChanged();
    }
    }
    catch (Exception e)
    {

    }
    mViewPager.setCurrentItem(tab.getPosition());
}

Étape 2: en utilisant la méthode statique, faites ce que vous voulez dans votre fragment,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}
Manoj Kumar
la source
1

J'utilise dans mon activité - KOTLIN

supportFragmentManager.addOnBackStackChangedListener {
                val f = supportFragmentManager.findFragmentById(R.id.fragment_container)

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
Álvaro Agüero
la source
0

Un fragment doit toujours être intégré dans une activité et le cycle de vie du fragment est directement affecté par le cycle de vie de l'activité hôte. Par exemple, lorsque l'activité est suspendue, tous les fragments s'y trouvent, et lorsque l'activité est détruite, tous les fragments le sont également

Zar E Ahmer
la source
0

onPause() La méthode fonctionne dans la classe d'activité que vous pouvez utiliser:

public void onDestroyView(){
super.onDestroyView    
}

dans le même but ..

Vikrant Kaloniya
la source
0

Suivez les étapes ci-dessous et vous obtiendrez la réponse nécessaire

1- Pour les deux fragments, créez un nouveau parent abstrait.
2- Ajoutez une méthode abstraite personnalisée qui devrait être implémentée par les deux.
3- Appelez-le depuis l'instance actuelle avant de la remplacer par la seconde.

Mostafa Magdy
la source
Comment cela aide-t-il dans la situation où un utilisateur retourne du fragment 2 au fragment 1?
CoolMind
0

Sur la base de la réponse de @Gor, j'ai écrit similaire dans Kotlin. Placez ce code dans onCreate()une activité. Cela fonctionne pour un fragment visible. Si vous avez ViewPagerdes fragments, il appellera ViewPagerle fragment de, pas un précédent.

supportFragmentManager.addOnBackStackChangedListener {
    supportFragmentManager.fragments.lastOrNull()?.onResume()
}

Après avoir lu https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6, j'ai compris qu'il serait préférable dans de nombreuses situations d'attacher de nouveaux fragments avec replace, non add. Ainsi, un besoin onResumedisparaîtra dans certains cas.

CoolMind
la source
0

Appel ft.replace doit déclencher onPause (du fragment remplacé) et onResume (du fragment de remplacement).

Je remarque que votre code se gonfle login_fragmentsur le fragment home et ne renvoie pas non plus les vues dans onCreateView. S'il s'agit de fautes de frappe, pouvez-vous montrer comment ces fragments sont appelés depuis votre activité?

David Refaeli
la source
Ce n'est pas une réponse, que se passe-t-il si "ajouter" est indispensable pour la conception de quelqu'un?!
Farid
0

Bien qu'avec un code différent, j'ai rencontré le même problème que l'OP, car j'ai utilisé à l'origine

fm.beginTransaction()
            .add(R.id.fragment_container_main, fragment)
            .addToBackStack(null)
            .commit();

au lieu de

fm.beginTransaction()
                .replace(R.id.fragment_container_main, fragment)
                .addToBackStack(null)
                .commit();

Avec "replace" le premier fragment est recréé lorsque vous revenez du deuxième fragment et donc onResume () est également appelé.

Greg Holst
la source
Ce n'est pas une réponse, que se passe-t-il si "ajouter" est indispensable pour la conception de quelqu'un?!
Farid
-2

Lors de la création d'une transaction de fragment, assurez-vous d'ajouter le code suivant.

// Replace whatever is in the fragment_container view with this fragment, 
// and add the transaction to the back stack 
transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 

Assurez-vous également de valider la transaction après l'avoir ajoutée au backstack

Mayur Plus
la source