Alternar entre la imagen de Android Navigation Drawer y Up caret cuando se utilizan fragmentos

Al usar el Cajón de navegación, los desarrolladores de Android recomiendan que en ActionBar “solo las pantallas representadas en el Cajón de navegación deberían tener la imagen del Cajón de navegación” y que “todas las demás pantallas tengan el quilate tradicional”.

Mira aquí para más detalles: http://youtu.be/F5COhlbpIbY

Estoy usando una actividad para controlar múltiples niveles de fragmentos y puedo hacer que la imagen del Cajón de navegación se muestre y funcione en todos los niveles.

Al crear fragmentos de nivel inferior, puedo llamar a ActionBarDrawerToggle setDrawerIndicatorEnabled(false) para ocultar la imagen del Cajón de navegación y mostrar el cursor Up

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag, "lowerFrag").addToBackStack(null).commit(); 

El problema que tengo es cuando vuelvo a los fragmentos de nivel superior que aún muestra el quilate hacia arriba en lugar de la imagen original del cajón de navegación. ¿Alguna sugerencia sobre cómo “actualizar” la Barra de acciones en los fragmentos de nivel superior para volver a mostrar la imagen del Cajón de navegación?


Solución

La sugerencia de Tom funcionó para mí. Esto es lo que hice:

Actividad principal

Esta actividad controla todos los fragmentos en la aplicación.

Al preparar nuevos fragmentos para reemplazar a otros, configuré el DrawerToggle setDrawerIndicatorEnabled(false) esta manera:

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag).addToBackStack(null).commit(); 

A continuación, en una anulación de onBackPressed , onBackPressed lo anterior configurando el DrawerToggle para setDrawerIndicatorEnabled(true) como este:

 @Override public void onBackPressed() { super.onBackPressed(); // turn on the Navigation Drawer image; // this is called in the LowerLevelFragments setDrawerIndicatorEnabled(true) } 

En los LowerLevelFragments

En los fragmentos modifiqué onCreate y onOptionsItemSelected como este:

En onCreate agregó setHasOptionsMenu(true) para habilitar la configuración del menú de opciones. También configure setDisplayHomeAsUpEnabled(true) para habilitar el < en la barra de acciones:

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // needed to indicate that the fragment would // like to add items to the Options Menu setHasOptionsMenu(true); // update the actionbar to show the up carat/affordance getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); } 

Luego en onOptionsItemSelected siempre que se presione <, se llama a onBackPressed() desde la actividad para subir un nivel en la jerarquía y visualizar la Imagen del Cajón de Navegación:

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed getActivity().onBackPressed(); return true; … } 

Usted ha escrito que, para implementar fragmentos de menor nivel, está reemplazando el fragmento existente, en lugar de implementar el fragmento de nivel inferior en una nueva actividad.

Creo que tendrías que implementar la funcionalidad de respaldo manualmente: cuando el usuario presionó hacia atrás tienes un código que muestra la stack (por ejemplo, en Activity::onBackPressed override). Entonces, donde sea que hagas eso, puedes invertir el setDrawerIndicatorEnabled .

Es fácil como 1-2-3.

Si quieres lograr:

1) Indicador de cajón : cuando no hay fragmentos en la stack posterior o se abre el cajón

2) Flecha : cuando algunos Fragmentos están en la Pila de Atrás

 private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } }; @Override protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, R.drawable.ic_navigation_drawer, 0, 0 ) { public void onDrawerClosed(View view) { syncActionBarArrowState(); } public void onDrawerOpened(View drawerView) { mDrawerToggle.setDrawerIndicatorEnabled(true); } }; mDrawerLayout.setDrawerListener(mDrawerToggle); getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener); } @Override protected void onDestroy() { getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener); super.onDestroy(); } private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0); } 

3) Ambos indicadores para actuar de acuerdo a su forma

 @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } else if (item.getItemId() == android.R.id.home && getSupportFragmentManager().popBackStackImmediate()) { return true; } else { return super.onOptionsItemSelected(item); } } 

PD Vea Crear un Cajón de navegación en Desarrolladores de Android en otros consejos sobre el comportamiento del indicador de 3 líneas.

He usado lo siguiente:

 getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if(getSupportFragmentManager().getBackStackEntryCount() > 0){ mDrawerToggle.setDrawerIndicatorEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } else { getSupportActionBar().setDisplayHomeAsUpEnabled(false); mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

Si el botón de la barra de acción no funciona, no olvide agregar el oyente:

 // Navigation back icon listener mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); 

Tengo algunos problemas para implementar una navegación de cajones con un botón de inicio, todo funcionó excepto el botón de acción.

Intente manejar la selección de elementos de inicio en MainActivity dependiendo del estado de DrawerToggle. De esta forma, no tiene que agregar el mismo código a cada fragmento.

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: onBackPressed(); return true; default: return super.onOptionsItemSelected(item); } } 

SEGUIR

La solución dada por @dzeikei es ordenada, pero se puede extender, cuando se usan fragmentos, para manejar automáticamente la configuración del indicador del cajón cuando la stack está vacía.

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.popBackStack(); // Make sure transactions are finished before reading backstack count fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } return true; default: return super.onOptionsItemSelected(item); } } 

EDITAR

Para la pregunta de @JJD.

Los fragmentos se mantienen / administran en una actividad. El código anterior se escribe una vez en esa actividad, pero solo maneja el cursor de arriba para onOptionsItemSelected .

En una de mis aplicaciones también necesitaba controlar el comportamiento de la herramienta de intercalación cuando presioné el botón Atrás. Esto se puede manejar anulando onBackPressed .

 @Override public void onBackPressed() { // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ super.onBackPressed(); } else { fragmentManager.executePendingTransactions(); fragmentManager.popBackStack(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } } }; 

Tenga en cuenta la duplicación de código entre onOptionsItemSelected y onBackPressed que se puede evitar creando un método y llamando a ese método en ambos lugares.

También tenga en cuenta que executePendingTransactions dos veces más executePendingTransactions que en mi caso era necesario o de lo contrario a veces tuve comportamientos extraños del up caret.

Creé una interfaz para la actividad de alojamiento para actualizar el estado de vista del menú de hamburguesas. Para los fragmentos de nivel superior establezco el alternar en true y para los fragmentos para los que quiero mostrar el up false .

 public class SomeFragment extends Fragment { public interface OnFragmentInteractionListener { public void showDrawerToggle(boolean showDrawerToggle); } private OnFragmentInteractionListener mListener; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { this.mListener = (OnFragmentInteractionListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onResume() { super.onResume(); mListener.showDrawerToggle(false); } } 

Entonces en mi actividad …

 public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener { private ActionBarDrawerToggle mDrawerToggle; public void showDrawerToggle(boolean showDrawerIndicator) { mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator); } } 

Esta respuesta estaba funcionando, pero había un pequeño problema con ella. El getSupportActionBar().setDisplayHomeAsUpEnabled(false) no fue llamado explícitamente y estaba causando que el ícono del cajón se ocultara incluso cuando no había elementos en el backstack, así que cambiar el método setActionBarArrowDependingOnFragmentsBackStack() funcionó para mí.

 private void setActionBarArrowDependingOnFragmentsBackStack() { int backStackEntryCount = getSupportFragmentManager() .getBackStackEntryCount(); // If there are no items in the back stack if (backStackEntryCount == 0) { // Please make sure that UP CARAT is Hidden otherwise Drawer icon // wont display getSupportActionBar().setDisplayHomeAsUpEnabled(false); // Display the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(true); } else { // Show the Up carat getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Hide the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(false); } } 

La lógica es clara. Mostrar el botón Atrás si la stack de fragmentos está clara. Muestre material de animación de devolución de hamburguesas si la stack de fragmentos no está clara.

 getSupportFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } } ); private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0); } //add these in Your NavigationDrawer fragment class public void setDrawerIndicatorEnabled(boolean flag){ ActionBar actionBar = getActionBar(); if (!flag) { mDrawerToggle.setDrawerIndicatorEnabled(false); actionBar.setDisplayHomeAsUpEnabled(true); mDrawerToggle.setHomeAsUpIndicator(getColoredArrow()); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } mDrawerToggle.syncState(); getActivity().supportInvalidateOptionsMenu(); } //download back button from this(https://www.google.com/design/icons/) website and add to your project private Drawable getColoredArrow() { Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp); Drawable wrapped = DrawableCompat.wrap(arrowDrawable); if (arrowDrawable != null && wrapped != null) { // This should avoid tinting all the arrows arrowDrawable.mutate(); DrawableCompat.setTint(wrapped, Color.GRAY); } return wrapped; } 

Si echas un vistazo a la aplicación GMAIL y vienes aquí para buscar el icono de carret / affordance …

Te pediría que hicieras esto, ninguna de las respuestas anteriores estaba clara. Pude modificar la respuesta aceptada.

  • NavigationDrawer -> Listview contiene subfragmentos.


  • los subfragmentos se enumerarán de esta manera

  • firstFragment == position 0 —> esto tendrá subfragmentos -> fragment

  • segundoFragmento
  • ThirdFragment y demás …

En firstFragment tienes otro fragmento.

Llamar esto en DrawerActivity

 getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if (getFragmentManager().getBackStackEntryCount() > 0) { mDrawerToggle.setDrawerIndicatorEnabled(false); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

y en fragmento

  setHasOptionsMenu(true); @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed activity.onBackPressed(); return true; } return false; } 

En el método de actividad OnBackPressed Drawer, establezca el alternador del cajón en true para habilitar nuevamente el ícono de la lista de navegación.

Gracias, Pusp

¡Puedes mirar este pequeño ejemplo! https://github.com/oskarko/NavDrawerExample

IMO, usando onNavigateUp () (como se muestra aquí ) en riwnodennyk’s o la solución de Tom es más limpia y parece funcionar mejor. Simplemente reemplace el código onOptionsItemSelected con esto:

 @Override public boolean onSupportNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { // handle up navigation return true; } else { return super.onSupportNavigateUp(); } }