El fragmento onResume () y onPause () no se llama en el backstack


195

Tengo un fragmento múltiple dentro de una actividad. Al hacer clic en un botón, estoy comenzando un nuevo fragmento, agregándolo al backstack. Naturalmente, esperaba que se llamara al onPause()método del Fragmento actual y al onResume()nuevo Fragmento. Pues no está pasando.

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

Lo que esperaba era

  1. Cuando se hace clic en el botón, LoginFragment se reemplaza con HomeFragment , onPause()de LoginFragment y onResume()de HomeFragment se llama
  2. Cuando se presiona la espalda, HomeFragment se poped y LoginFragment se ve, y onPause()de HomeFragment y onResume()de LoginFragment se llama.

Lo que estoy obteniendo es

  1. Cuando se hace clic en el botón, HomeFragment reemplaza correctamente LoginFragment , se llama a onResume () de HomeFragment , pero nunca se llama a onPause () de LoginFragment .
  2. Cuando se vuelve a presionar, HomeFragment aparece correctamente para revelar LoginFragment , se llama a onPause () de HomeFragment , pero onResume () de LoginFragment nunca se llama.

¿Es este el comportamiento normal? ¿Por qué es onResume()de LoginFragment no conseguir llama cuando se presiona el botón de retroceso.


Agregue el código de actividad que maneja los fragmentos.
blessenm

Tengo el problema de muestra, en pausa no me llamaron, ¿cómo resolvieron esto?
Sam

Tuve el mismo problema pero me di cuenta de que estaba usando ft2.add (); en lugar de ft2.replace (). La otra razón sería si su actividad mantiene una referencia al fragmento (agregándolo a una colección o asignándolo a una variable de clase)
Friggles

3
Estoy teniendo el mismo problema. Noté que .replace () llamará a los métodos necesarios del ciclo de vida, pero esencialmente destruye el fragmento. Además, onSaveInstanceState no se llama. Como tal, no puedo mantener su estado. Entonces, necesito usar add, pero onResume / Pause no se llama :(
ariets

FWIW, mi experiencia es que los fragmentos de la biblioteca de soporte llaman a onPause y onResume cuando empujan / abren la pila, pero los fragmentos integrados de Android no lo hacen. Todavía no he encontrado una solución adecuada para eso.
benkc

Respuestas:


185

Los fragmentos onResume()o onPause()serán llamados solo cuando se llamen las Actividades onResume()o onPause(). Están estrechamente acoplados a la Activity.

Lea la sección Manejo del ciclo de vida del fragmento de este artículo .


1
En el artículo, se dice que "una vez que la actividad alcanza el estado reanudado, puede agregar y eliminar libremente fragmentos de la actividad. Por lo tanto, solo mientras la actividad está en el estado reanudado puede cambiar el ciclo de vida de un fragmento de forma independiente". ¿Significa esto que el fragmento onResume se puede llamar incluso si no se llama a la actividad onResume?
v4r

3
en cuanto a los fragmentos nativos (no compatibles) en 4.4 (no estoy seguro si es cierto para versiones anteriores) se llama a onPause () y onResume () no solo cuando estos eventos ocurren en la actividad, sino por ejemplo cuando llama a replace () o add () / remove () mientras realiza la transacción, por lo que esta respuesta es engañosa, al menos para las versiones recientes de Android.
Dmide

19
Según ese documento, el fragmento en realidad debería moverse al estado detenido cuando se cambia a la pila. Pero no solo no se llama a onPause y onResume, ni a onStop y onStart, ni tampoco a ningún otro método del ciclo de vida. Entonces la guía es definitivamente engañosa.
benkc

1
Aunque no está relacionado, encontré esta pregunta mientras buscaba mi problema de onPause()ser llamado después en onSaveInstanceState()lugar de antes. Esto se puede reproducir si usted a un niño diferente en mi FragmentStatePagerAdapter(digamos que se mueve del niño 0 al niño 2, tenga en cuenta que esto sucede porque el niño 0 se destruye cuando se abre el niño 2 )
Sufian

No estoy seguro de lo que quieres decir. Llamar a ft.replace debería activar onPause (del fragmento reemplazado) y onResume (del fragmento reemplazado). Esto se hace independientemente de cualquier actividad ...
David Refaeli

20
  • Como ha utilizado ft2.replace(), FragmentTransaction.remove() se llama al método y Loginfragmentse eliminará. Consulte esto . Así que onStop()de LoginFragmentserá llamado en lugar de onPause(). (Como el nuevo fragmento reemplaza completamente al antiguo).
  • Pero como también lo ha usado ft2.addtobackstack(), el estado del Loginfragmentse guardará como un paquete y cuando haga clic en el botón Atrás desde HomeFragment, onViewStateRestored()se llamará seguido de onStart()de LoginFragment. Entonces eventualmente onResume()no se llamará.

1
onViewStateRestoredse llama si tienessetRetainInstance(true)
Farid

14

Aquí está mi versión más robusta de la respuesta de Gor (el uso de fragments.size () no es confiable debido a que el tamaño no se reduce después de que el fragmento aparece)

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

Y el método '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

Si realmente desea reemplazar un fragmento dentro de otro fragmento, debe usar Fragmentos anidados .

En su código debe reemplazar

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
            }

        }
    });

pero tenga cuidado si hay más de un fragmento visible


Gracias, funciona, pero si solo un fragmento es visible (entonces, ViewPagercon fragmentos hará referencia a sus fragmentos).
CoolMind

3

Tengo un código muy similar al tuyo y si funciona en Pause () y onResume (). Al cambiar el fragmento, estas funciones se activan respectivamente.

Código en fragmento:

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

}

Iniciar sesión cuando cambio de fragmento:

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

Fragmento de 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;
}

Acción cuando vuelvo a pulsar (en la actividad donde cargo el fragmento):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }

2

Lo que hago en fragmento infantil:

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

Y luego anular onResume en ParentFragment


1
Nunca debe llamar a los métodos de ciclo de vida manualmente, especialmente uno dentro del otro
interrupción

@breakline esta técnica funciona. ¿Tienes alguna otra manera?
Vikash Parajuli

Sí, debe agregar su propia implementación para llamar porque el sistema también llama a los métodos del ciclo de vida y si llama a los métodos del ciclo de vida uno dentro del otro de esta manera, podría (y muy probablemente lo hará) problemas posteriores.
corte

1

Simplemente no puede agregar un fragmento a un fragmento. Esto debe suceder en FragmentActivity. Supongo que está creando el LoginFragment en una FragmentActivity, por lo que para que esto funcione, debe agregar HomeFragment a través de FragmentActivity cuando se cierra el inicio de sesión.

El punto general es que necesita una clase FragmentActivity desde donde agrega cada Fragment al FragmentManager. No es posible hacer esto dentro de una clase Fragment.


Sí, están dentro de una actividad fragmentaria.
Krishnabhadra

Si los fragmentos se agregan dinámicamente, puede agregar tantos fragmentos como desee a un fragmento, pero no a los definidos en la etiqueta xml <fragment>
Argumento ilegal

1

Si agrega el fragmento en XML, no puede intercambiarlos dinámicamente. Lo que sucede es que son demasiado, por lo que los eventos no se disparan como cabría esperar. El problema está documentado en esta pregunta. Reemplazar FragmenManager hace superposición

Convierta middle_fragment en FrameLayout y cárguelo como se muestra a continuación y sus eventos se dispararán.

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

1

Puedes probar esto,

Paso 1: anula el método Tabselected en tu actividad

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

Paso 2: Usando el método estático haz lo que quieras en tu fragmento,

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

1

Yo uso en mi actividad - KOTLIN

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

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

0

Un fragmento siempre debe estar incrustado en una actividad y el ciclo de vida del fragmento se ve directamente afectado por el ciclo de vida de la actividad del host. Por ejemplo, cuando la actividad está en pausa, también lo están todos los fragmentos, y cuando la actividad se destruye, también lo están todos los fragmentos.


0

onPause() El método funciona en la clase de actividad que puede usar:

public void onDestroyView(){
super.onDestroyView    
}

para el mismo propósito ..


0

Siga los pasos a continuación y obtendrá la respuesta necesaria.

1- Para ambos fragmentos, cree un nuevo padre abstracto.
2- Agregue un método abstracto personalizado que ambos deben implementar.
3- Llámalo desde la instancia actual antes de reemplazarla por la segunda.


¿Cómo ayuda en una situación en la que un usuario regresa del fragmento 2 al fragmento 1?
CoolMind

0

Basado en la respuesta de @Gor , escribí algo similar en Kotlin. Coloque este código en onCreate()una actividad. Funciona para un fragmento visible. Si tiene ViewPagerfragmentos, llamará ViewPageral fragmento, no al anterior.

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

Después de leer https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 , entendí que sería mejor en muchas situaciones adjuntar nuevos fragmentos replace, no add. Entonces una necesidad enonResume algunos casos desaparecerá.


0

Vocación ft.replace deben activar onPause (del fragmento reemplazado) y onResume (del fragmento reemplazado).

Noto que su código se infla login_fragmenten el fragmento de inicio y tampoco devuelve las vistas en onCreateView. Si se trata de errores tipográficos, ¿puede mostrar cómo se llaman estos fragmentos desde su actividad?


Esto no es una respuesta, ¿qué pasa si "agregar" es imprescindible para el diseño de alguien?
Farid

0

Aunque con un código diferente, experimenté el mismo problema que el OP, porque originalmente usé

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

en vez de

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

Con "reemplazar", el primer fragmento se recrea cuando regresa del segundo fragmento y, por lo tanto, también se llama a onResume ().


Esto no es una respuesta, ¿qué pasa si "agregar" es imprescindible para el diseño de alguien?
Farid

-2

Mientras crea una transacción fragmentada, asegúrese de agregar el siguiente código.

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

También asegúrese de confirmar la transacción después de agregarla al backstack

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.