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 boolean
valor. Necesitamos imponer un valor booleano porque necesitaremos saber si presionar el botón Atrás ha "absorbido" el evento Atrás. Devolver true
significa que sí, y no se necesita ninguna otra acción, de lo contrario, false
dice 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 Fragment
objeto 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, ArrayOutOfBoundsException
podrí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 Backable
interfaz 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 Backable
interfaz. 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.OnKeyListner
interfaz ...
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 BackableFragment
capturará automáticamente los clics de regreso utilizando la View.OnKeyListener
interfaz. 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 super
mé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 void
porque 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