navegacion configuración botones accesibilidad android android-fragments navigation-drawer android-navigationview

configuración - botones de navegacion android



Implementación adecuada de la navegación hacia atrás y el manejo del botón de inicio usando la barra de herramientas en Android (6)

Estoy usando una actividad única y varios fragmentos (captura de pantalla adjunta) dentro de la misma actividad para proporcionar una navegación perfecta. Pero después de implementar la última barra de herramientas y la vista de navegación, parece difícil manejar los botones de navegación y de inicio. Estoy teniendo problemas con las siguientes cosas.

  • Manejando el botón Hamburguesa / Atrás en la parte superior izquierda. Alternar el icono y la funcionalidad a Menú y Atrás.
    • Título de la página: cambiar los títulos de las páginas cada vez que se envía un fragmento y se abre.

He intentado varias cosas como anular OnBackPressed (), setHomeAsUpIndicator, hacer saltar fragmentos manualmente. Anteriormente, estaba usando la palanca ActionBarDrawer para manejar esto, pero ahora está fallando de alguna manera. Revisé las muestras de Google que parecen usar actividades separadas en la mayoría de los lugares.

¿Alguien me puede orientar sobre cómo implementar una navegación hacia atrás adecuada para manejar el botón de navegación y el botón Atrás en los fragmentos internos y los títulos de las páginas? Estoy usando AppCompatActivity , android.app.Fragment , NavigationView y la barra de herramientas .


"Título de la página: cambiar los títulos de las páginas cada vez que se envía un fragmento"

Cuando eliminas un fragmento, existe el método isRemoving() . Ayuda a cambiar el título de nuevo.

@Override public void onStop() { super.onStop(); if (isRemoving()) { // Change your title here } }

"Funcionalidad para Menu y Back Nav"

Sugerencia: tenemos que confiar en el sistema de navegación predeterminado de Android. Si usamos addToBackStack() para nuestros fragmentos, en teoría no tenemos que anular onBackPressed () en absoluto.

  1. "La aplicación no redefine la función esperada de un ícono del sistema (como el botón Atrás)".
  2. "La aplicación es compatible con la navegación con el botón Atrás del sistema estándar y no hace uso de ningún mensaje personalizado en la pantalla" Botón Atrás ".

Calidad de la aplicación principal: https://developer.android.com/distribute/essentials/quality/core.html

"Administrar el botón Hamburguesa / Atrás en la parte superior izquierda"

Sugiero usar la actividad en lugar de ''MainActivityDetailFragment'' para evitar complicaciones.


Agregue esto en su MainActivity donde está llamando Fragmentos. getBackStackEntryCount() Devuelve el número de fragmentos en la pila trasera. donde el fragmento en la parte inferior de la pila tiene un índice 0. popBackStack() el fragmento superior de la pila trasera

@Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { if (getSupportFragmentManager().getBackStackEntryCount() == 1) { getSupportFragmentManager().popBackStack(); } else { super.onBackPressed(); } } return true; }

Y en tu Fragmento donde quieres volver usa esta función

@Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { getActivity().onBackPressed(); } return true; }


Es mucho más fácil de ilustrar con algún tipo de división de responsabilidad para su Activity y Fragment .

Problema 1: Administrar el botón Hamburger / Back en la parte superior izquierda. Alternar el icono y la funcionalidad a Menú y Atrás.

A partir de la ilustración, la solución debe encapsularse en la Activity , que se verá así:

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private ActionBarDrawerToggle mDrawerToggle; private DrawerLayout mDrawer; private ActionBar mActionBar; private boolean mToolBarNavigationListenerIsRegistered = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); mActionBar = getSupportActionBar(); mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); mDrawer.addDrawerListener(mDrawerToggle); mDrawerToggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); // On orientation change savedInstanceState will not be null. // Use this to show hamburger or up icon based on fragment back stack. if(savedInstanceState != null){ resolveUpButtonWithFragmentStack(); } else { // You probably want to add your ListFragment here. } } @Override public void onBackPressed() { if (mDrawer.isDrawerOpen(GravityCompat.START)) { mDrawer.closeDrawer(GravityCompat.START); } else { int backStackCount = getSupportFragmentManager().getBackStackEntryCount(); if (backStackCount >= 1) { getSupportFragmentManager().popBackStack(); // Change to hamburger icon if at bottom of stack if(backStackCount == 1){ showUpButton(false); } } else { super.onBackPressed(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } else if (id == android.R.id.home) { // Home/Up logic handled by onBackPressed implementation onBackPressed(); } return super.onOptionsItemSelected(item); } @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { // Handle navigation view item clicks here. int id = item.getItemId(); // Navigation drawer item selection logic goes here mDrawer.closeDrawer(GravityCompat.START); return true; } private void replaceFragment() { /** * Your fragment replacement logic goes here * e.g. * FragmentTransaction ft = getFragmentManager().beginTransaction(); * String tag = "MyFragment"; * ft.replace(R.id.content, MyFragment.newInstance(tag), tag).addToBackStack(null).commit(); */ // The part that changes the hamburger icon to the up icon showUpButton(true); } private void resolveUpButtonWithFragmentStack() { showUpButton(getSupportFragmentManager().getBackStackEntryCount() > 0); } private void showUpButton(boolean show) { // To keep states of ActionBar and ActionBarDrawerToggle synchronized, // when you enable on one, you disable on the other. // And as you may notice, the order for this operation is disable first, then enable - VERY VERY IMPORTANT. if(show) { // Remove hamburger mDrawerToggle.setDrawerIndicatorEnabled(false); // Show back button mActionBar.setDisplayHomeAsUpEnabled(true); // when DrawerToggle is disabled i.e. setDrawerIndicatorEnabled(false), navigation icon // clicks are disabled i.e. the UP button will not work. // We need to add a listener, as in below, so DrawerToggle will forward // click events to this listener. if(!mToolBarNavigationListenerIsRegistered) { mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); mToolBarNavigationListenerIsRegistered = true; } } else { // Remove back button mActionBar.setDisplayHomeAsUpEnabled(false); // Show hamburger mDrawerToggle.setDrawerIndicatorEnabled(true); // Remove the/any drawer toggle listener mDrawerToggle.setToolbarNavigationClickListener(null); mToolBarNavigationListenerIsRegistered = false; } // So, one may think "Hmm why not simplify to: // ..... // getSupportActionBar().setDisplayHomeAsUpEnabled(enable); // mDrawer.setDrawerIndicatorEnabled(!enable); // ...... // To re-iterate, the order in which you enable and disable views IS important #dontSimplify. } }

Problema 2: Título de la página: cambiar los títulos de las páginas cada vez que se envía un fragmento y se abre.

Esencialmente, esto se puede manejar en el onStart de cada Fragment es decir, su ListFragment, DetailsFragment y CommentsFragment tienen un aspecto similar al siguiente:

@Override public void onStart() { super.onStart(); // where mText is the title you want on your toolbar/actionBar getActivity().setTitle(mText); }

Probablemente vale la pena tener setRetainInstance(true) en el onCreate de tus fragmentos también.


Intenta algo como esto:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); if (getSupportActionBar()!=null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } drawer = (DrawerLayout) findViewById(R.id.drawer_layout); final ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(drawerToggle); drawerToggle.syncState(); final View.OnClickListener originalToolbarListener = drawerToggle.getToolbarNavigationClickListener(); final View.OnClickListener navigationBackPressListener = new View.OnClickListener() { @Override public void onClick(View v) { getFragmentManager().popBackStack(); } }; getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if (getFragmentManager().getBackStackEntryCount() > 0) { drawerToggle.setDrawerIndicatorEnabled(false); drawerToggle.setToolbarNavigationClickListener(navigationBackPressListener); } else { drawerToggle.setDrawerIndicatorEnabled(true); drawerToggle.setToolbarNavigationClickListener(originalToolbarListener); } } }); // Though below steps are not related but I have included to show drawer close on Navigation Item click. navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem item) { int id = item.getItemId(); /** * handle item clicks using id */ drawer.closeDrawer(GravityCompat.START); return true; } }); }

Manejar el estado del cajón onBackPressed :

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

Para volver a cargar el fragment anterior en la prensa trasera, siempre agregue la transacción del fragmento a la pila trasera de esta manera:

FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); SomeFragment fragmentToBeLoaded = new SomeFragment(); fragmentTransaction.replace(R.id.fragment_container, fragmentToBeLoaded, fragmentToBeLoaded.getName()); fragmentTransaction.addToBackStack(fragmentToBeLoaded.getName()); fragmentTransaction.commit();

Para cambiar dinámicamente el título de la página, puede llamarlo desde el método onStart o onResume de cada Fragment :

@Override public void onStart() { super.onStart(); getActivity().setTitle("Title for fragment"); }

Nota: He considerado la declaración de diseño estándar y, por lo tanto, no he incluido ningún diseño.


Ok, después de muchas pruebas finalmente logré configurar una buena navegación. Necesitaba exactamente lo mismo que tú, la única diferencia es que estoy usando Fragmentos v4, pero no creo que esto cambie nada aquí.

No estoy usando ActionBarDrawerToggle ya que los últimos ejemplos de Google ya no usan este componente.

La solución a continuación también funciona para la navegación profunda: actividad principal -> fragmento -> fragmento, etc.

El único cambio necesario en los Fragmentos es cambiar el título:

@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getActivity().setTitle(R.string.targets); }

En el método principal de Actividad sobre onCreate , inicializo lo siguiente:

mNavigationView = (NavigationView) findViewById(R.id.navigation_view); setupDrawerContent(mNavigationView); final Toolbar toolbar = (Toolbar) findViewById(R.id.drawer_toolbar); setSupportActionBar(toolbar); getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_24);// Set the hamburger icon getSupportActionBar().setDisplayHomeAsUpEnabled(true);// Set home button pressable // Handle the changes on the actionbar getSupportFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { public void onBackStackChanged() { // When no more fragments to remove, we display back the hamburger icon and the original activity title if (getSupportFragmentManager().getBackStackEntryCount() <= 0) { getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_24); setTitle(R.string.app_name); } // Else displays the back arrow else { getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_arrow_back_24); } } });

Aquí está el código para manejar la acción en el botón Inicio:

@Override public boolean onOptionsItemSelected(MenuItem item){ // Close the soft keyboard right away Tools.setSoftKeyboardVisible(mViewPager, false); switch (item.getItemId()) { case android.R.id.home: // When no more fragments to remove, open the navigation drawer if (getSupportFragmentManager().getBackStackEntryCount() <= 0) { mDrawerLayout.openDrawer(GravityCompat.START); } // Removes the latest fragment else { getSupportFragmentManager().popBackStack(); } return true; } return super.onOptionsItemSelected(item); }

Y finalmente el código para manejar la acción de la prensa de vuelta:

@Override public void onBackPressed() { // When no more fragments to remove, closes the activity if (getSupportFragmentManager().getBackStackEntryCount() <= 0) { super.onBackPressed(); } // Else removes the latest fragment else { getSupportFragmentManager().popBackStack(); } }

NOTA : Estoy usando una AppCompatActivity , una NavigationView y el tema Theme.AppCompat.Light.NoActionBar .


tl; dr

Vea esto: https://youtu.be/ANpBWIT3vlU

Clone esto: https://github.com/shredderskelton/androidtemplate .

Este es un problema muy común y que he superado creando un tipo de proyecto de plantilla que utilizo cada vez que comienzo un nuevo proyecto de Android. La idea es abstraer la mayor parte de la lógica que maneja el botón de retroceso, el indicador de ''hamburguesa'' y la administración de fragmentos en clases reutilizables:

Comience creando una clase BaseActivity y BaseFragment. Aquí es donde vas a obtener la mayor cantidad posible de código reutilizable.

Comencemos con tu BaseActividad

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentManager = getSupportFragmentManager(); fragmentHandler = new AddFragmentHandler(fragmentManager); fragmentManager.addOnBackStackChangedListener(backStackListener); }

El FragmentManager es la clave para poseer la pila trasera, por lo que necesita escuchar los cambios en la pila trasera desde aquí. El AddFramentHandler es una clase pequeña que preparé para facilitar la adición de fragmentos, desde fragmentos. Más sobre eso más adelante.

@Override public void onBackPressed() { if (sendBackPressToDrawer()) { //the drawer consumed the backpress return; } if (sendBackPressToFragmentOnTop()) { // fragment on top consumed the back press return; } //let the android system handle the back press, usually by popping the fragment super.onBackPressed(); //close the activity if back is pressed on the root fragment if (fragmentManager.getBackStackEntryCount() == 0) { finish(); } }

onBackPressed es donde ocurre la mayor parte de la magia. Observa el formato de texto simple de los métodos. Soy un gran fan de Clean Code . Si necesita escribir comentarios, su código no está limpio. Básicamente, necesitas tener un lugar central donde puedas correr cuando no estás seguro de por qué no presionas el botón de retroceso de la manera que esperas. Este método es ese lugar.

private void syncDrawerToggleState() { ActionBarDrawerToggle drawerToggle = getDrawerToggle(); if (getDrawerToggle() == null) { return; } if (fragmentManager.getBackStackEntryCount() > 1) { drawerToggle.setDrawerIndicatorEnabled(false); drawerToggle.setToolbarNavigationClickListener(navigationBackPressListener); //pop backstack } else { drawerToggle.setDrawerIndicatorEnabled(true); drawerToggle.setToolbarNavigationClickListener(drawerToggle.getToolbarNavigationClickListener()); //open nav menu drawer } }

Esta es la otra parte clave de la BaseActivity. Básicamente, este método verifica si se encuentra en el fragmento raíz y configura el indicador en consecuencia. Observe que cambia el oyente dependiendo de cuántos fragmentos hay en la pila trasera.

Luego está el BaseFragment:

@Override public void onResume() { super.onResume(); getActivity().setTitle(getTitle()); } protected abstract String getTitle();

El código anterior muestra cómo se maneja el título por los fragmentos.