Ocultar el cajón de navegación cuando el usuario presiona el botón de retroceso

He seguido los tutoriales oficiales de Google para crear un cajón de navegación.

Por el momento, todo funciona bien, excepto cuando el usuario utiliza el botón de retroceso nativo que Android proporciona en la parte inferior de la pantalla (junto con los botones de inicio y de la aplicación reciente). Si el usuario vuelve a navegar utilizando este botón nativo, el cajón de navegación seguirá abierto. Si el usuario navega de nuevo usando la barra de acción, el cajón de navegación se cerrará como yo quiero que sea.

Mi código es casi idéntico a los tutoriales oficiales, excepto por cómo manejo el usuario seleccionando un elemento en el cajón:

mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { switch(position) { case 0: { Intent intent = new Intent(MainActivity.this, NextActivity.class); startActivity(intent); } } } }); 

¿Cómo puedo hacer que el cajón de navegación se cierre cuando el usuario navega de regreso usando el botón de retroceso nativo? Cualquier consejo apreciado. ¡Gracias!

Tienes que sobrescribir onBackPressed () . De los documentos:

Se llama cuando la actividad ha detectado la pulsación del usuario de la tecla de retroceso. La implementación predeterminada simplemente termina la actividad actual, pero puede anularla para hacer lo que quiera.

Así que puedes tener código como este:

 @Override public void onBackPressed() { if (this.drawerLayout.isDrawerOpen(GravityCompat.START)) { this.drawerLayout.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } 

Si está abierto este método lo cierra, de lo contrario vuelve al comportamiento predeterminado.

onBackPressed() anular onBackPressed() en su actividad y comprobar la condición donde está abierto el cajón de navegación. Si está abierto, luego ciérrelo, de lo contrario, haga un método de retorno normal. Aquí hay algunos códigos mezclados con algún pseudocódigo para ayudarte:

 @Override public void onBackPressed(){ if(drawer.isDrawerOpen()){ //replace this with actual function which returns if the drawer is open drawer.close(); // replace this with actual function which closes drawer } else{ super.onBackPressed(); } } 

Para reemplazar el aspecto de pseudocódigo en la documentación del cajón. Sé que ambos métodos existen.

Utilizando una implementación de la respuesta proporcionada por @James Cross trabajado, pero la animación para cerrar el cajón era indeseable y unfixable sin mucho molestia, ejemplo .

 @Override public void onResume() { super.onResume(); mDrawerLayout.closeDrawers(); } 

Una solución es reiniciar la actividad cuando se pulsa el botón de retroceso del dispositivo. No me parece ideal, pero funciona. onBackPressed() , como sugiere @ mt0s y @Qazi Ahmed y pasando un extra para determinar la actividad que llama:

  mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { switch(position) { case 0: { Intent intent = new Intent(MainActivity.this, NextActivity.class); //pass int extra to determine calling activity intent.putExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY); startActivity(intent); } } } }); 

En NextActivity.class , compruebe la actividad de llamada:

 @Override public void onBackPressed() { int callingActivity = getIntent().getIntExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY); switch(callingActivity) { case CallingActivityInterface.MAIN_ACTIVITY: { Intent intent = new Intent(this, MainActivity.class); startActivity(intent); finish(); } ... } } 

De esta manera el cajón se cierra sin animación cuando vuelvo a MainActivity sin importar si utilizo el botón hacia arriba o hacia atrás. Probablemente hay mejores maneras de hacer esto. Mi aplicación es relativamente simple en este momento y esto funciona, pero espero un método más eficaz si alguien tiene uno.

ACTUALIZAR:

A partir de la biblioteca de soporte 24.0.0 esto es posible sin ninguna solución. Se han añadido dos nuevos métodos openDrawer y closeDrawer a DrawerLayout que permiten abrir o cerrar el cajón sin animación.

Ahora puede utilizar openDrawer(drawerView, false) y closeDrawer(drawerView, false) para abrir y cerrar el cajón sin demora.


Si llama a startActivity() sin llamar a closeDrawer() , el cajón se dejará abierto en esa instancia de la actividad cuando vuelva a ella utilizando el botón de retroceso. Llamar closeDrawer() al llamar a startActivity() tiene varios problemas, que van desde la animación intermitente hasta un retardo perceptual largo, dependiendo de la solución que utilice. Así que estoy de acuerdo en que el mejor enfoque es simplemente llamar startActivity() y luego cerrar el cajón al regresar.

Para hacer este trabajo bien, necesitas una manera de cerrar el cajón sin una animación cercana al navegar de nuevo a la actividad con el botón de la parte posterior. (Una solución relativamente inútil sería simplemente forzar la actividad a recreate() al navegar hacia atrás, pero es posible resolver esto sin hacer eso).

También debe asegurarse de cerrar el cajón sólo si vuelve después de navegar, y no después de un cambio de orientación, pero eso es fácil.


Detalles

(Puede pasar esta explicación si sólo desea ver el código.)

Aunque llamar a closeDrawer() de onCreate() hará que el cajón comience cerrado sin ninguna animación, lo mismo no es cierto de onResume() . Llamar closeDrawer() de onResume() cerrará el cajón con una animación momentáneamente visible para el usuario. DrawerLayout no proporciona ningún método para cerrar el cajón sin esa animación, pero es posible ampliarlo para agregar uno.

Cerrando el cajón en realidad sólo se desliza fuera de la pantalla, por lo que puede saltar con eficacia la animación moviendo el cajón directamente a su posición "cerrada". La dirección de la traducción varía según la gravedad (si es un cajón izquierdo o derecho), y la posición exacta depende del tamaño del cajón una vez que se ha colocado con todos sus niños.

Sin embargo, simplemente moverlo no es suficiente, ya que DrawerLayout mantiene algún estado interno en LayoutParams extendido que utiliza para saber si el cajón está abierto. Si simplemente mueve el cajón fuera de la pantalla, no sabrá que está cerrado, y eso causará otros problemas. (Por ejemplo, el cajón volverá a aparecer en el siguiente cambio de orientación.)

Puesto que está compilando la biblioteca de soporte en su aplicación, puede crear una clase en el paquete android.support.v4.widget para obtener acceso a sus partes predeterminadas (paquete privado) o ampliar DrawerLayout sin copiar sobre ninguna de las otras Clases que necesita. Esto también reducirá la carga de actualizar su código con los cambios futuros en la biblioteca de soporte. (Siempre es mejor aislar el código de los detalles de implementación tanto como sea posible). Puede usar moveDrawerToOffset() para mover el cajón y configurar LayoutParams para que sepa que el cajón está cerrado.


Código

Este es el código que saltará la animación:

  // move drawer directly to the closed position moveDrawerToOffset(drawerView, 0.f); // set internal state so DrawerLayout knows it's closed final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 0.f; lp.knownOpen = false; invalidate(); 

Nota: si solo llama a moveDrawerToOffset() sin cambiar LayoutParams , el cajón volverá a su posición abierta en el siguiente cambio de orientación.


Opción 1 (uso de DrawerLayout existente)

Este método agrega una clase de utilidad al paquete support.v4 para obtener acceso a las partes privadas del paquete que necesitamos dentro de DrawerLayout.

Coloque esta clase en / src / android / support / v4 / widget /:

 package android.support.v4.widget; import android.support.annotation.IntDef; import android.support.v4.view.GravityCompat; import android.view.Gravity; import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class Support4Widget { /** @hide */ @IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END}) @Retention(RetentionPolicy.SOURCE) private @interface EdgeGravity {} public static void setDrawerClosed(DrawerLayout drawerLayout, @EdgeGravity int gravity) { final View drawerView = drawerLayout.findDrawerWithGravity(gravity); if (drawerView == null) { throw new IllegalArgumentException("No drawer view found with gravity " + DrawerLayout.gravityToString(gravity)); } // move drawer directly to the closed position drawerLayout.moveDrawerToOffset(drawerView, 0.f); // set internal state so DrawerLayout knows it's closed final DrawerLayout.LayoutParams lp = (DrawerLayout.LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 0.f; lp.knownOpen = false; drawerLayout.invalidate(); } } 

Establezca un booleano en su actividad cuando navegue lejos, indicando que el cajón debe estar cerrado:

 public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER"; private boolean mCloseNavDrawer; @Override public void onCreate(Bundle savedInstanceState) { // ... if (savedInstanceState != null) { mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER); } } @Override public boolean onNavigationItemSelected(MenuItem menuItem) { // ... startActivity(intent); mCloseNavDrawer = true; } @Override public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer); super.onSaveInstanceState(savedInstanceState); } 

… y usa el método setDrawerClosed() para cerrar el cajón en onResume() sin animación:

 @Overrid6e protected void onResume() { super.onResume(); if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START); mCloseNavDrawer = false; } } 

Opción 2 (se extiende desde DrawerLayout)

Este enfoque extiende DrawerLayout para agregar un método setDrawerClosed ().

Coloque esta clase en / src / android / support / v4 / widget /:

 package android.support.v4.widget; import android.content.Context; import android.support.annotation.IntDef; import android.support.v4.view.GravityCompat; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class CustomDrawerLayout extends DrawerLayout { /** @hide */ @IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END}) @Retention(RetentionPolicy.SOURCE) private @interface EdgeGravity {} public CustomDrawerLayout(Context context) { super(context); } public CustomDrawerLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setDrawerClosed(View drawerView) { if (!isDrawerView(drawerView)) { throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer"); } // move drawer directly to the closed position moveDrawerToOffset(drawerView, 0.f); // set internal state so DrawerLayout knows it's closed final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 0.f; lp.knownOpen = false; invalidate(); } public void setDrawerClosed(@EdgeGravity int gravity) { final View drawerView = findDrawerWithGravity(gravity); if (drawerView == null) { throw new IllegalArgumentException("No drawer view found with gravity " + gravityToString(gravity)); } // move drawer directly to the closed position moveDrawerToOffset(drawerView, 0.f); // set internal state so DrawerLayout knows it's closed final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams(); lp.onScreen = 0.f; lp.knownOpen = false; invalidate(); } } 

Utilice CustomDrawerLayout lugar de DrawerLayout en los diseños de actividad:

 <android.support.v4.widget.CustomDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" > 

… y establecer un booleano en su actividad cuando navegue lejos, indicando que el cajón debe ser cerrado:

 public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER"; private boolean mCloseNavDrawer; @Override public void onCreate(Bundle savedInstanceState) { // ... if (savedInstanceState != null) { mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER); } } @Override public boolean onNavigationItemSelected(MenuItem menuItem) { // ... startActivity(intent); mCloseNavDrawer = true; } @Override public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer); super.onSaveInstanceState(savedInstanceState); } 

… y use el método setDrawerClosed() para cerrar el cajón en onResume() sin animación:

 @Overrid6e protected void onResume() { super.onResume(); if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.setDrawerClosed(GravityCompat.START); mCloseNavDrawer = false; } } 

Es probable que desee asegurarse de que el sorteo de navegación siempre está cerrado cuando se abre la actividad. Usa esto para hacer eso:

 @Override public void onResume(){ mDrawerList.closeDrawer(Gravity.LEFT); } 

Aquí hay una solución alternativa a su problema.

 @Override public void onBackPressed(){ if(drawerLayout.isDrawerOpen(navigationView)){ drawerLayout.closeDrawer(navigationView); }else { finish(); } } 

¿Por qué la molestia? Simplemente cierre el cajón cuando haga clic en un elemento del cajón. Así es como se hace en la aplicación oficial de Google Play.

 private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { drawerLayout.closeDrawer(GravityCompat.START, false); selectItem(position); } } 
  • Intermodule (proyectos de biblioteca) en la aplicación android
  • Cajón de navegación de Android sobre las pestañas
  • La forma correcta de manejar la navegación "Up" de acuerdo con las directrices
  • Cómo obtener direcciones flecha sin utilizar google maps api
  • Android: inicia la navegación de Google desde una aplicación
  • ¿Cómo extender la vista de la disposición del cajón?
  • Abra el segundo juego de cajones como un subconjunto de cajones durante el primer
  • Restauración del estado del fragmento al cambiar fragmentos a través de la barra de navegación inferior
  • Navegación de actividades de Android guardar estado
  • ¿Cómo puedo añadir un solo divisor después de algunos elementos para separar un grupo en RecyclerView?
  • ¿Cómo navegar a la actividad de almacenamiento masivo USB de Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.