¿Cómo implementar onBackPressed () en Fragments?

460

¿Hay alguna manera de implementar onBackPressed()en Android Fragment similar a la implementación en Android Activity?

Como el ciclo de vida del fragmento no tiene onBackPressed(). ¿Existe algún otro método alternativo para anular los onBackPressed()fragmentos de Android 3.0?

Android_programmer_camera
fuente
13
En mi humilde opinión, un fragmento no debe saber ni preocuparse por el botón ATRÁS. Una actividad podría preocuparse por el botón ATRÁS, aunque con fragmentos que normalmente maneja FragmentManager. Pero dado que el fragmento no sabe acerca de su entorno más amplio (por ejemplo, si es uno de los varios en la actividad), no es realmente seguro que un fragmento intente determinar qué es la funcionalidad BACK adecuada.
CommonsWare
61
¿Qué tal cuando todo el fragmento lo hace para mostrar un WebView, y desea que el WebView "regrese" a la página anterior cuando se presiona el botón Atrás?
mharper
La respuesta de Michael Herbig fue perfecta para mí. Pero para aquellos que no pueden usar getSupportFragmentManager (). Utilice getFragmentManager () en su lugar.
Dantalian
La respuesta de Dmitry Zaitsev en [Pregunta similar] [1] funciona bien. [1]: stackoverflow.com/a/13450668/1372866
ashakirov
66
para responder a mharper, puede hacer algo como webview.setOnKeyListener (nuevo OnKeyListener () {@Override public boolean onKey (View v, int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) {webview.goBack (); return true;} return false;}});
Omid Aminiva

Respuestas:

307

Resolví de esta manera anular onBackPresseden la Actividad. Todos los FragmentTransactionson addToBackStackantes de comprometerse:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}
Hw.Master
fuente
mismo problema stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
44
count == 1 permitirá cerrar el primer fragmento al presionar un botón de retroceso. Pero por lo demás, la mejor solución
Kunalxigxag
2
Si está utilizando la biblioteca de soporte v7 y su actividad se extiende desde FragmentActivity (o una subclase, como AppCompatActivity), esto sucederá de forma predeterminada. Vea FragmentActivity # onBackPressed
Xiao
3
pero no puede usar variables, clases y funciones de la clase de fragmento en este código
user25
2
@Prabs si está utilizando el fragmento v4 de soporte, asegúrese de estar usando getSupportFragmentManager (). GetBackStackEntryCount ();
Veer3383
152

En mi opinión, la mejor solución es:

SOLUCIÓN JAVA

Crear interfaz simple:

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

Y en tu actividad

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

Finalmente en tu Fragmento:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

SOLUCIÓN KOTLIN

1 - Crear interfaz

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Prepara tu actividad

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3 - Implementar en tu Fragmento objetivo

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}
Maxime Jallu
fuente
1
¿Qué es R.id.main_container? ¿Es esa la identificación para el FragmentPager?
Nathan F.
1
@MaximeJallu en la solución de Kotlin, mezclar llamadas seguras ( .?) y forzar no nulos ( !!) puede generar NPE: el reparto no siempre es seguro. Prefiero escribir if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }o más kotliney(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek
1
@Antek Hola, gracias por tu regreso, podrías haber editado la publicación para hacer tu corrección. No juzgue severamente la solución general.
Maxime Jallu
44
¿No debería ser .onBackPressed()?.takeIf { !it }?.let{...}? .not()solo devuelve el inverso.
David Miguel
1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }Esto es para personas que usan Kotlin y NavigationController
Pavle Pavlov
97

De acuerdo con @HaMMeRed, la respuesta aquí es pseudocódigo cómo debería funcionar. Digamos que su actividad principal se llama BaseActivityque tiene fragmentos secundarios (como en el ejemplo de SlidingMenu lib). Aquí están los pasos:

Primero necesitamos crear una interfaz y una clase que implemente su interfaz para tener un método genérico

  1. Crear interfaz de clase OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. Crear clase que implemente habilidades de OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. Desde ahora, trabajaremos en nuestro código BaseActivityy sus fragmentos.

  4. Crea un oyente privado en la cima de tu clase BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. crear un método para configurar el oyente en BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. en anulación onBackPressedimplementar algo así

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. en su fragmento en onCreateViewusted debe agregar nuestro oyente

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

Voila, ahora cuando haces clic de nuevo en el fragmento, debes capturar tu método personalizado de respaldo.

pez muerto
fuente
Si más de un fragmento es un oyente, ¿cómo determina, en onBackPressed()qué fragmento se estaba mostrando cuando se presionó el botón Atrás?
Al Lelopath
2
puedes personalizar click listener en lugar de nueva baseBackPressedListener y llamar allí como anónimo para establecer su propio comportamiento, algo como esto:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish
Gracias, he cambiado mi posición sobre esto, aunque recomendaría desacoplar los mensajes de difusión locales ahora. Creo que produciría componentes de mayor calidad que están muy desacoplados.
HaMMeReD
isue here stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
Esto funcionó perfectamente para mí, reinicio la actividad
raphaelbgr
86

Esto funcionó para mí: https://stackoverflow.com/a/27145007/3934111

@Override
public void onResume() {
    super.onResume();

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}
Rodrigo Rodrigues
fuente
2
Trabajado sin problemas! Pero también debe manejar la acción predeterminada, probablemente escribiendo una condición IF más. Desde entonces, estaba bajando mi slideUpPanel si estaba expandido. Entonces, primero tuve que marcar un cheque si (el panel está arriba) luego .....
sud007
18
El problema con esta solución es que getView () solo devuelve la vista principal (y no la subvista, si obtienen el foco). Entonces, si tiene un EditText y escribe algo de texto, presione "Atrás" una vez para ocultar el teclado, la segunda pulsación en "Atrás" se inicia desde el "EditText" (una vista en sí) y este método no parece captar el " presione el botón "Atrás" (ya que solo captura la vista principal). Según tengo entendido, podría captar los eventos de pulsación de tecla en su "EditText", así como cualquier otra vista, pero si tiene un formulario "complejo", entonces esto no es tan limpio y fácil de mantener como podría ser.
Pelpotronic
Esta es una gran solución simple para la pesadilla que son los fragmentos. Si tiene un formulario de edición de texto 'complejo', entonces probablemente debería eliminar su proyecto y comenzar de nuevo. Devuelva falso cuando quiera usar el comportamiento estándar. Vuelva a ser verdadero cuando haya interceptado la
tecla de
65

Si quisieras ese tipo de funcionalidad, deberías anularla en tu actividad y luego agregar una YourBackPressedinterfaz a todos tus fragmentos, que invocas en el fragmento relevante cada vez que presionas el botón Atrás.

Editar: me gustaría agregar mi respuesta anterior.

Si tuviera que hacer esto hoy, usaría una transmisión, o posiblemente una transmisión ordenada si esperaba que otros paneles se actualicen al unísono al panel de contenido principal / principal.

LocalBroadcastManageren la Biblioteca de soporte puede ayudar con esto, y usted simplemente envía la transmisión onBackPressedy se suscribe en sus fragmentos que les importa. Creo que la mensajería es una implementación más desacoplada y se escalaría mejor, por lo que sería mi recomendación oficial de implementación ahora. Simplemente use la Intentacción de 's como filtro para su mensaje. envíe su recién creado ACTION_BACK_PRESSED, envíelo desde su actividad y escúchelo en los fragmentos relevantes.

HaMMeReD
fuente
44
Idea bastante ingeniosa! algunos pensamientos adicionales: + es importante darse cuenta de esta lógica con la transmisión yendo desde fragmentos de actividad> (no al revés). OnBackPressed solo está disponible en el nivel de actividad, por lo que capturar el evento allí y transmitirlo a todos los fragmentos de "escucha" es clave aquí. + ¡Usar un EventBus (como Square's Otto) hace que la implementación de un caso como este sea trivial!
Kaushik Gopal
2
Todavía no he usado el EventBus de Square, pero estaré en productos futuros. Creo que es una mejor solución de puro Java, y si puedes eliminar las secciones de Android de tu arquitectura, mucho mejor.
HaMMeReD
¿Cómo dejas que solo los fragmentos superiores manejen el evento? por ejemplo, querrías destruir el fragmento que se muestra actualmente
eugene
El receptor de transmisión debe estar vinculado al ciclo de vida si lo está utilizando, por lo que cuando no está visible no debería recibir el evento.
HaMMeReD
También tenga en cuenta que LocalBroadcastManagerno se pueden hacer transmisiones ordenadas
Xiao
46

Nada de eso es fácil de implementar ni funcionará de manera óptima.

Los fragmentos tienen un método llamado onDetach que hará el trabajo.

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

ESTO HARÁ EL TRABAJO.

Sterling Diaz
fuente
20
pero el problema aquí es que no sabe si el fragmento se desprendió debido a una presión hacia atrás o debido a alguna otra acción, lo que llevó al usuario a realizar alguna otra actividad o fragmento.
Soleado
77
Se llama a onDetach () cuando el fragmento ya no está asociado a su actividad. Esto se llama después de onDestroy (). Aunque obtendría este código cuando presiona el botón Atrás, también obtendrá este código cuando cambie de fragmento a fragmento. Sin embargo, podrías hacer algo ingenioso para que esto funcione ...
apmartin1991
Funciona bien para un DialogFragment.
CoolMind
2
No olvide agregar isRemoving()como se describe en stackoverflow.com/a/27103891/2914140 .
CoolMind
1
esto está mal. Se puede llamar por varias razones
Bali
40

Si está utilizando androidx.appcompat:appcompat:1.1.0o superior, puede agregar un OnBackPressedCallbackfragmento a su fragmento de la siguiente manera

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

Ver https://developer.android.com/guide/navigation/navigation-custom-back

aaronmarino
fuente
Si está usando androidx-core-ktx, puede usarrequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max
Es importante tener en cuenta que el LifecycleOwnerparámetro debe agregarse como en este ejemplo. Sin él, cualquier fragmento iniciado después llamará handleBackPressed()si se presiona el botón Atrás.
Eric B.
¿Es este método obligatorio con la biblioteca de navegación?
Silencio
25

Simplemente agregue addToBackStackmientras realiza la transición entre sus fragmentos como a continuación:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

si escribe addToBackStack(null), lo manejará solo, pero si le da una etiqueta, debe manejarlo manualmente.

Rudi
fuente
¿Hay algo más que deba hacerse o es suficiente agregar el método addToBackStack?
Sreecharan Desabattula
1
si solo agrega que debería funcionar, si aún no funciona, debería haber algo en su código. Deje el código en algún lugar para ver por favor @SreecharanDesabattula
Rudi
@Rudi puedes decir stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
20

Como esta pregunta y algunas de las respuestas tienen más de cinco años, permítanme compartir mi solución. Este es un seguimiento y modernización de la respuesta de @oyenigun

ACTUALIZACIÓN: Al final de este artículo, agregué una implementación alternativa usando una extensión de Fragmento abstracto que no involucrará a la Actividad en absoluto, lo que sería útil para cualquier persona con una jerarquía de fragmentos más compleja que involucre fragmentos anidados que requieran un comportamiento de espalda diferente.

Necesitaba implementar esto porque algunos de los fragmentos que uso tienen vistas más pequeñas que me gustaría descartar con el botón Atrás, como pequeñas vistas de información que aparecen, etc., pero esto es bueno para cualquiera que necesite anular el comportamiento de el botón de retroceso dentro de los fragmentos.

Primero, defina una interfaz

public interface Backable {
    boolean onBackPressed();
}

Esta interfaz, a la que llamo Backable(soy un fanático de las convenciones de nomenclatura), tiene un método único onBackPressed()que debe devolver un booleanvalor. Necesitamos imponer un valor booleano porque necesitaremos saber si presionar el botón Atrás ha "absorbido" el evento Atrás. Devolver truesignifica que sí, y no se necesita ninguna otra acción, de lo contrario, falsedice que la acción de retroceso predeterminada todavía debe llevarse a cabo. Esta interfaz debe ser su propio archivo (preferiblemente en un paquete separado llamado interfaces). Recuerde, separar sus clases en paquetes es una buena práctica.

Segundo, encuentra el fragmento superior

Creé un método que devuelve el último Fragmentobjeto en la pila posterior. Yo uso etiquetas ... si usa identificaciones, haga los cambios necesarios. Tengo este método estático en una clase de utilidad que se ocupa de estados de navegación, etc. pero, por supuesto, ponlo donde más te convenga. Para la edificación, he puesto el mío en una clase llamada NavUtils.

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

Asegúrese de que el recuento de la pila posterior sea mayor que 0, de lo contrario, ArrayOutOfBoundsExceptionpodría lanzarse en tiempo de ejecución. Si no es mayor que 0, devuelve nulo. Verificaremos un valor nulo más tarde ...

Tercero, implementar en un fragmento

Implemente la Backableinterfaz en cualquier fragmento donde necesite anular el comportamiento del botón de retroceso. Agregue el método de implementación.

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

En la onBackPressed()anulación, ponga cualquier lógica que necesite. Si desea que el botón de nuevo a no saca la pila de vuelta (el comportamiento predeterminado), volverá cierto , que su evento posterior que se haya absorbido. De lo contrario, devuelve falso.

Por último, en tu actividad ...

Anule el onBackPressed()método y agregue esta lógica:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

Obtenemos el fragmento actual en la pila posterior, luego hacemos una comprobación nula y determinamos si implementa nuestra Backableinterfaz. Si es así, determine si el evento fue absorbido. Si es así, hemos terminado onBackPressed()y podemos volver. De lo contrario, trátelo como un retroceso normal y llame al método super.

Segunda opción para no involucrar a la actividad

A veces, no desea que la Actividad maneje esto en absoluto, y necesita manejarlo directamente dentro del fragmento. Pero, ¿quién dice que no puede tener Fragments con una API de retroceso? Simplemente extienda su fragmento a una nueva clase.

Cree una clase abstracta que extienda Fragment e implemente la View.OnKeyListnerinterfaz ...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

Como puede ver, cualquier fragmento que se extienda BackableFragmentcapturará automáticamente los clics de regreso utilizando la View.OnKeyListenerinterfaz. Simplemente llame al onBackButtonPressed()método abstracto desde dentro del onKey()método implementado usando la lógica estándar para discernir la presión de un botón de retroceso. Si necesita registrar clics de teclas que no sean el botón de retroceso, solo asegúrese de llamar al supermétodo al anular onKey()su fragmento, de lo contrario, anulará el comportamiento en la abstracción.

Fácil de usar, solo extienda e implemente:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

Dado que el onBackButtonPressed()método en la superclase es abstracto, una vez que lo extienda, debe implementarlo onBackButtonPressed(). Regresa voidporque solo necesita realizar una acción dentro de la clase de fragmento y no necesita retransmitir la absorción de la prensa de nuevo a la Actividad. Asegúrese de llamar al onBackPressed()método Actividad si lo que está haciendo con el botón Atrás no requiere manejo, de lo contrario, el botón Atrás se desactivará ... ¡y no quiere eso!

Advertencias Como puede ver, esto establece al oyente clave en la vista raíz del fragmento, y tendremos que enfocarlo. Si hay textos de edición involucrados (o cualquier otra vista de robo de foco) en su fragmento que extiende esta clase (u otros fragmentos internos o vistas que tengan la misma), deberá manejar eso por separado. Hay un buen artículo sobre cómo extender un EditText para perder el foco en una pulsación hacia atrás.

Espero que alguien encuentre esto útil. Feliz codificación

mwieczorek
fuente
Gracias, es una buena solución. ¿Puedes decir por qué llamas super.onBackPressed();dos veces?
CoolMind
Solo se llama una vez por escenario con respecto al estado nulo de currentFragment. Si el fragmento no es nulo y el fragmento implementa la Backableinterfaz, no se toman medidas. Si el fragmento no es nulo y NO se implementa Backable, llamamos al método super. Si el fragmento es nulo, salta a la última súper llamada.
mwieczorek
Lo siento, no entendí. En el caso de que a currentFragmentno sea nulo y sea una instancia de Backable y se separe (presionamos el backbotón y cerramos el fragmento) super.onBackPressed();se llama la primera aparición de , luego la segunda.
CoolMind
Excelente solución
David Toledo
Quizás "Closeable" suena un poco mejor que "Backable"
pumnao
15

La solución es simple:

1) Si tiene una clase de fragmento base que se extienden todos los fragmentos, agregue este código a su clase, de lo contrario, cree una clase de fragmento base

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2) En su clase de Actividad, anule onBackPressed de la siguiente manera:

private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}

3) En su clase Fragmento, agregue el código deseado:

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }
IdoT
fuente
9

onBackPressed() hacer que el fragmento se desprenda de la actividad.

Según la respuesta de @Sterling Diaz, creo que tiene razón. PERO alguna situación estará mal. (ej. Girar pantalla)

Entonces, creo que podríamos detectar si isRemoving()alcanzar objetivos.

Puedes escribirlo en onDetach()o onDestroyView(). Eso es trabajo.

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}
林奕 忠
fuente
8

Bueno, lo hice así, y funcionó para mí.

Interfaz simple

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Implementación de ejemplo

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

luego simplemente anula onBackPressed en actividad

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}
Błażej
fuente
No funciona ! super.onbackpressed () recibe llamadas siempre :(
Animesh Mangla
Cómo pasar este evento Fragmento interno. Tengo ViewPager en Actividad y ese ViewPager tiene Fragmentos, ahora todos esos Fragmentos tienen fragmentos secundarios. Cómo pasar el evento del botón de retroceso a los fragmentos secundarios.
Kishan Vaghela
@KishanVaghela bueno, no toqué Android desde entonces, pero tengo una idea. Dame 24 para actualizar mi respuesta.
Błażej
Ok, no hay problema, una opción es que necesito pasar el evento de escucha a cada fragmento secundario del fragmento principal. pero no es una forma ideal porque necesito hacer esto para cada fragmento.
Kishan Vaghela
@KishanVaghela Estaba pensando en el gerente en actividad. Puede agregar / eliminar fragmentos con esa interfaz. Pero con este enfoque, debe implementar el administrador en cada fragmento que tenga subfragmentos. Entonces, tal vez una mejor solución sea crear un administrador como singleton. Luego tendrá que agregar / eliminar fragmentos / objetos y en 'onBackPressed' usted llama al método 'onBackPressed' de este administrador. Ese método llamará al método 'onClick' en cada objeto que se agregó al administrador. ¿Eso es más o menos claro para ti?
Błażej
8

Debe agregar una interfaz a su proyecto como se muestra a continuación;

public interface OnBackPressed {

     void onBackPressed();
}

Y luego, debe implementar esta interfaz en su fragmento;

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

Y puede activar este evento onBackPressed en sus actividades en el evento onBackPressed como se muestra a continuación;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}
oyenigun
fuente
Es un buen método, pero tenga cuidado, no llame getActivity().onBackPressed();ya que invocará una excepción: "java.lang.StackOverflowError: tamaño de pila 8MB".
CoolMind
8

Este es solo un pequeño código que hará el truco:

 getActivity().onBackPressed();

Espero que ayude a alguien :)

Shahid Sarwar
fuente
44
Pidió anular el comportamiento, no simplemente invocar el método de actividad.
mwieczorek
2
Lo leí cuidadosamente, gracias. Su solución simplemente retransmite el método onBackPressed nuevamente a la Actividad, cuando le preguntó cómo anular.
mwieczorek
7

Si usa EventBus, probablemente sea una solución mucho más simple:

En tu fragmento:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

y en tu clase de Actividad puedes definir:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java es solo un objeto POJO

Esto es súper limpio y no hay problemas de interfaz / implementación.

Akshat
fuente
7

Esta es mi solución:

en MyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

y en Fragmento:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});
Hamidreza Samadi
fuente
6

Usando el componente de navegación puede hacerlo así :

Java

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

Kotlin

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}
B-GangsteR
fuente
4

¿Qué tal usar onDestroyView ()?

@Override
public void onDestroyView() {
    super.onDestroyView();
}
El mejor artista
fuente
55
¿Qué pasa con ir a otro fragmento del fragmento real, qué pasará con su método? :)
Choletski
La respuesta más inútil. Es lo mismo que escribir i = io if (1 < 0) {}.
CoolMind
4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}
Gururaj Hosamani
fuente
Solo si los fragmentos no tienen una vista enfocable (como editar texto), deje que los fragmentos manejen el botón de retroceso como lo hace esta respuesta. De lo contrario, deje que la actividad que contiene fragmentos lo haga OnBackPress () mientras se escribe la primera respuesta; ya que la vista enfocable robará el foco del fragmento. Sin embargo, podemos recuperar el enfoque para el fragmento mediante un enfoque claro en todas las vistas enfocables mediante el método clearFocus () y volver a enfocar al fragmento.
Nguyen Tan Dat
4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

en su fragmento agregue lo siguiente, no olvide implementar la interfaz de mainactivity.

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}
joelvarma
fuente
4

En mi solución (Kotlin);

Estoy usando la función onBackAlternative como parámetro en BaseActivity .

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

Tengo una función para configurar onBackPressAlternative en BaseFragment .

Fragmento Base

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

Entonces mi onBackPressAlternative está listo para usar en fragmentos.

Sub Fragmentos

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}
Asignar
fuente
3

No implemente el método ft.addToBackStack () para que cuando presione el botón Atrás, su actividad finalice.

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Muhammad Aamir Ali
fuente
3

Solo sigue estos pasos:

Siempre al agregar un fragmento,

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

Luego, en la actividad principal, anular onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

Para manejar el botón de retroceso en su aplicación,

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

¡Eso es!

Divya
fuente
3

En el ciclo de vida de la actividad, siempre el botón de retroceso de Android se ocupa de las transacciones de FragmentManager cuando usamos FragmentActivity o AppCompatActivity.

Para manejar el backstack no necesitamos manejar su conteo de backstack ni etiquetar nada, pero debemos mantener el enfoque al agregar o reemplazar un fragmento. Encuentre los siguientes fragmentos para manejar los botones de retroceso,

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

Aquí, no agregaré back stack para mi fragmento de inicio porque es la página de inicio de mi aplicación. Si agrega addToBackStack a HomeFragment, la aplicación esperará para eliminar todo el fragmento en la actividad y luego obtendremos una pantalla en blanco, así que mantengo la siguiente condición,

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

Ahora, puede ver el fragmento previamente agregado en la actividad y la aplicación se cerrará cuando llegue a HomeFragment. También puede consultar los siguientes fragmentos.

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}
Deva
fuente
3

Fragmento: crea un Fragmento base colocando un método:

 public boolean onBackPressed();

Actividad:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

Su actividad se ejecutará sobre los fragmentos adjuntos y visibles y llamará a onBackPressed () en cada uno de ellos y cancelará si uno de ellos devuelve 'verdadero' (lo que significa que se ha manejado, por lo que no hay más acciones).

Alécio Carvalho
fuente
Señor, esta es la forma más elegante y funciona perfecto.
asozcan
3

De acuerdo con las notas de la versión de AndroidX , androidx.activity 1.0.0-alpha01se lanza y presenta ComponentActivityuna nueva clase base de las existentes FragmentActivityy AppCompatActivity. Y esta versión nos trae una nueva característica:

Ahora puede registrar una OnBackPressedCallbackvía addOnBackPressedCallbackpara recibir onBackPressed()devoluciones de llamada sin necesidad de anular el método en su actividad.

TonnyL
fuente
¿Puedes mostrar un ejemplo de esto? No estoy obteniendo ese método para resolver.
Bryan Bryce
1
@BryanBryce No he encontrado un ejemplo, pero el documento oficial: developer.android.com/reference/androidx/activity/…
TonnyL
El método no resolverá b / c AppCompatActivity en realidad se extiende desde androidx.core.app.ComponentActivity en lugar de androidx.activity.ComponentActivity.
wooldridgetm
3

Puede registrar un fragmento en la actividad para manejar la presión posterior:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

uso:

En fragmento:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

En actividad:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}
Francis
fuente
3

Proporcionar una navegación hacia atrás personalizada al manejar onBackPressed ahora es más fácil con devoluciones de llamada dentro del fragmento.

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

Si desea que la acción de retroceso predeterminada se base en alguna condición, puede usar:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();
krishnakumarcn
fuente
3

Dentro del método onCreate del fragmento, agregue lo siguiente:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}
David Elmasllari
fuente
después de agregar esta contrapresión myFragment funciona, pero ahora la contrapresión de actividad no lo hizo
Sunil Chaudhary
2

Mejor solución,

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
Shervin Gharib
fuente