Il frammento onResume () e onPause () non viene richiamato nello backstack


196

Ho più frammenti all'interno di un'attività. Con un clic sul pulsante sto iniziando un nuovo frammento, aggiungendolo allo backstack. Mi aspettavo naturalmente che fosse chiamato il onPause()metodo dell'attuale frammento e onResume()del nuovo frammento. Beh, non sta succedendo.

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();
  }
}

Quello che mi aspettavo era

  1. Quando si fa clic sul pulsante, LoginFragment viene sostituito con HomeFragment , onPause()di LoginFragment e onResume()di HomeFragment vengono chiamati
  2. Quando si preme di nuovo, HomeFragment è poped fuori e LoginFragment è visto, e onPause()di HomeFragment e onResume()di LoginFragment viene chiamato.

Quello che sto ottenendo è

  1. Quando si fa clic sul pulsante, HomeFragment è correttamente sostituendo LoginFragment , onResume () di HomeFragment si chiama, ma onPause () di LoginFragment non viene mai chiamato.
  2. Quando torna premuto, HomeFragment sia correttamente popping per rivelare LoginFragment , onPause () di HomeFragment viene chiamato, ma onResume () di LoginFragment non viene mai chiamato.

È questo il comportamento normale? Perché onResume()di LoginFragment non sempre chiamato quando si preme il pulsante indietro.


Aggiungi il codice attività che gestisce i frammenti.
blessenm,

sto avendo il problema del campione, in pausa non essere chiamato, come hai risolto questo problema,
Sam

Ho avuto lo stesso problema ma mi sono reso conto che stavo usando ft2.add (); anziché ft2.replace (). Un altro motivo sarebbe se la tua attività sta mantenendo un riferimento al frammento (aggiungendolo a una raccolta o assegnandolo a una variabile di classe)
Friggles

3
Sto avendo lo stesso problema. Ho notato che .replace () chiamerà i metodi del ciclo di vita necessari, ma essenzialmente distrugge il frammento. Inoltre, onSaveInstanceState non viene chiamato. Come tale, non posso mantenere il suo stato. Quindi, devo usare aggiungi, ma onResume / Pause non si chiama :(
ariets

FWIW, la mia esperienza è che i frammenti della libreria di supporto richiamano onPause e onResume quando si spinge / fa scoppiare il backstack, ma i frammenti integrati di Android no. Non ho ancora trovato una soluzione adeguata per quello.
Benkc,

Risposte:


187

I frammenti onResume()o onPause()verranno chiamati solo quando viene chiamato Attività onResume()o onPause(). Sono strettamente accoppiati al Activity.

Leggi la sezione Gestione del ciclo di vita dei frammenti di questo articolo .


1
Nell'articolo si dice che "una volta che l'attività raggiunge lo stato ripreso, è possibile aggiungere e rimuovere liberamente frammenti all'attività. Pertanto, solo mentre l'attività è nello stato ripreso, il ciclo di vita di un frammento può cambiare in modo indipendente". Questo significa che il frammento onResume può essere chiamato anche se l'attività onResume non viene chiamata?
v4r,

3
come per i frammenti nativi (non supportati) in 4.4 (non sono sicuro che sia vero per le versioni precedenti) onPause () e onResume () vengono chiamati non solo quando si verificano questi eventi nell'attività, ma ad esempio quando si chiama sostituisci () o aggiungi () / remove () durante l'esecuzione della transazione, quindi questa risposta è fuorviante almeno per le versioni recenti di Android.
Dmide,

19
Secondo tale documento, il frammento dovrebbe effettivamente essere spostato nello stato di arresto quando scambiato nel backstack. Ma non solo onPause e onResume non vengono chiamati, né sono onStop e onStart - o, del resto, altri metodi del ciclo di vita. Quindi la guida è decisamente fuorviante.
Benkc,

1
Anche se non correlato, ho trovato questa domanda mentre cercavo il mio problema di onPause()essere chiamato dopo onSaveInstanceState()invece che prima. Questo può essere riprodotto se sei in un bambino diverso nel mio FragmentStatePagerAdapter(supponiamo che passi da bambino 0 a bambino 2, nota a se stesso, ciò accade perché il bambino 0 viene distrutto quando il bambino 2 si apre )
Sufian,

Non sono sicuro cosa intendi. Chiamare ft.replace dovrebbe attivare onPause (del frammento sostituito) e onResume (del frammento sostitutivo). Questo viene fatto indipendentemente da qualsiasi attività ...
David Refaeli,

20
  • Dal momento che hai usato ft2.replace(), FragmentTransaction.remove() viene chiamato il metodo e Loginfragmentverrà rimosso. Fare riferimento a questo . Così onStop()di LoginFragmentsarà chiamato al posto di onPause(). (Poiché il nuovo frammento sostituisce completamente quello vecchio).
  • Ma poiché hai anche usato ft2.addtobackstack(), lo stato del Loginfragmentsarà salvato come un pacchetto e quando fai clic sul pulsante indietro da HomeFragment, onViewStateRestored()verrà chiamato seguito da onStart()di LoginFragment. Quindi alla fine onResume()non verrà chiamato.

1
onViewStateRestoredviene chiamato se haisetRetainInstance(true)
Farid

14

Ecco la mia versione più robusta della risposta di Gor (l'uso di fragments.size () non è affidabile a causa della dimensione non ridotta dopo la comparsa del frammento)

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. 
                    }
                }
            }
        }
    });

E il metodo '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;
}

9

Se vuoi davvero sostituire un frammento all'interno di un altro frammento, dovresti usare Nested Fragments .

Nel tuo codice è necessario sostituire

final FragmentManager mFragmentmanager =  getFragmentManager();

con

final FragmentManager mFragmentmanager =  getChildFragmentManager();

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
            }

        }
    });

ma fai attenzione se sono visibili più frammenti


Grazie, funziona, ma se è visibile un solo frammento (quindi, ViewPagercon i frammenti farà riferimento ai suoi frammenti).
CoolMind

3

Ho un codice molto simile al tuo e se funziona suPause () e onResume (). Quando si cambia il frammento, queste funzioni sono attivate rispettivamente.

Codice in frammento:

 @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");

}

Registra quando cambia frammento:

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

Frammento 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;
}

Azione quando torno indietro (nell'attività in cui carico il frammento):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }

2

Cosa faccio nel frammento di bambino:

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

E quindi sovrascrivi onResume su ParentFragment


1
Non dovresti mai chiamare manualmente i metodi del ciclo di vita, specialmente l'uno dentro l'altro
interrompi il

@breakline questa tecnica funziona. Hai un altro modo?
Vikash Parajuli,

Sì, dovresti aggiungere la tua implementazione per chiamare perché i metodi del ciclo di vita sono chiamati anche dal sistema e se chiami i metodi del ciclo di vita uno all'interno dell'altro in questo modo potresti (e molto probabilmente lo farai) causare problemi successivi.
breakline il

1

Semplicemente non puoi aggiungere un frammento a un frammento. Questo deve accadere in FragmentActivity. Presumo che tu stia creando LoginFragment in un FragmentActivity, quindi per farlo funzionare devi aggiungere HomeFragment tramite FragmentActivity quando il login si chiude.

Il punto generale è che hai bisogno di una classe FragmentActivity da cui aggiungi ogni frammento al FragmentManager. Non è possibile farlo all'interno di una classe di frammenti.


Sì, sono dentro un'attività frammentaria.
Krishnabhadra

se i frammenti vengono aggiunti in modo dinamico, è possibile aggiungere tutti i frammenti desiderati a un frammento ma non a quelli definiti nel tag <fragment> xml
Argomento illegale

1

Se aggiungi il frammento in XML, non puoi scambiarli dinamicamente. Quello che succede è che sono eccessivamente, quindi gli eventi non si attivano come ci si aspetterebbe. Il problema è documentato in questa domanda.FragmenManager sostituisce crea sovrapposizioni

Trasforma middle_fragment in un FrameLayout e caricalo come sotto e i tuoi eventi verranno attivati.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();

1

Puoi provare questo,

Passaggio 1: Sostituisci il metodo Tabselected nella tua attività

@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());
}

Passaggio 2: utilizzando il metodo statico fai quello che vuoi nel tuo frammento,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}

1

Sulla base della risposta di @Gor, ho scritto simili in Kotlin. Inserire questo codice in onCreate()un'attività. Funziona per un frammento visibile. Se hai ViewPagercon i frammenti, chiamerà ViewPageril frammento, non uno precedente.

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

Dopo aver letto https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 ho capito che sarebbe meglio in molte situazioni allegare nuovi frammenti replace, no add. Quindi onResumein alcuni casi un bisogno sparirà.


1

Uso nella mia attività - KOTLIN

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

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }

0

Un frammento deve sempre essere incorporato in un'attività e il ciclo di vita del frammento è direttamente influenzato dal ciclo di vita dell'attività dell'host. Ad esempio, quando l'attività viene messa in pausa, lo sono anche tutti i frammenti e quando l'attività viene distrutta, lo sono anche tutti i frammenti


0

onPause() Il metodo funziona nella classe di attività che è possibile utilizzare:

public void onDestroyView(){
super.onDestroyView    
}

per lo stesso scopo ..


0

Segui i passaggi seguenti e otterrai la risposta necessaria

1- Per entrambi i frammenti, creane uno nuovo genitore astratto.
2- Aggiungi un metodo astratto personalizzato che dovrebbe essere implementato da entrambi.
3- Chiamalo dall'istanza corrente prima di sostituirla con la seconda.


In che modo aiuta una situazione quando un utente ritorna dal frammento 2 al frammento 1?
CoolMind

0

chiamata ft.replace dovrebbe attivare onPause (del frammento sostituito) e onResume (del frammento sostitutivo).

Vedo che il tuo codice si gonfia login_fragmentsul frammento home e inoltre non restituisce le visualizzazioni in onCreateView. Se questi sono errori di battitura, puoi mostrare come vengono chiamati questi frammenti all'interno della tua attività?


Questa non è una risposta, e se "aggiungi" è necessario per la progettazione di qualcuno ?!
Farid

0

Sebbene con un codice diverso, ho riscontrato lo stesso problema dell'OP, perché originariamente usato

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

invece di

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

Con "sostituisci" il primo frammento viene ricreato quando ritorni dal secondo frammento e quindi viene anche chiamato onResume ().


Questa non è una risposta, e se "aggiungi" è necessario per la progettazione di qualcuno ?!
Farid

-2

Durante la creazione di una transazione di frammenti, assicurarsi di aggiungere il seguente codice.

// 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); 

Assicurati inoltre di eseguire il commit della transazione dopo averla aggiunta al backstack

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.