¿Manejar el título de ActionBar con la pila de fragmentos?

8 minutos de lectura

Yo tengo un Activity donde cargo en un ListFragment y, al hacer clic, profundiza un nivel y un nuevo tipo de ListFragment se muestra, reemplazando el original (usando el showFragment método a continuación). Esto se coloca en la pila trasera.

Al principio, la actividad muestra el título predeterminado en la barra de acción (es decir, se configura automáticamente en función de la configuración de la aplicación). android:label).

Al mostrar la lista para el siguiente nivel en la jerarquía, el nombre del elemento en el que se hizo clic debe convertirse en el título de la barra de acción.

Sin embargo, al presionar atrás, me gustaría restaurar el título predeterminado original. esto no es algo FragmentTransaction conoce, por lo que el título no se restaura.

He leído vagamente sobre FragmentBreadCrumbs, pero esto parece requerir el uso de una vista personalizada. Estoy usando ActionBarSherlock y preferiría no tener mi propia vista de título personalizada.

Cual es la mejor manera de hacer esto? ¿Es posible sin una carga de código repetitivo y sin tener que realizar un seguimiento de los títulos que se muestran en el camino?


protected void showFragment(Fragment f) {
  FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  ft.replace(R.id.fragment_container, f);
  ft.addToBackStack(null);
  ft.commit();
}

avatar de usuario
Warpzit

En cada fragmento y cada actividad cambio el título así. De esta forma el título activo siempre será correcto:

@Override
public void onResume() {
    super.onResume();
    // Set title
    getActivity().getActionBar()
        .setTitle(R.string.thetitle);
}

Hay algunos casos en los que onResume no se llama dentro de fragmentos. En algunos de estos casos podemos utilizar:

public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser) {
        // Set title
        getActivity().getActionBar()
            .setTitle(R.string.thetitle);
    }
}

  • No tuve necesidad de lanzar, ni obtener la barra de acción (es decir, solo llamo getActivity().setTitle(...)), pero este es un enfoque razonable. Gracias.

    – Christopher Orr

    23 de noviembre de 2012 a las 19:37


  • -1. ¡Este enfoque es incorrecto! De acuerdo con las pautas de diseño de Android, al cambiar fragmentos usando el cajón de navegación, el título de la barra de acción solo debe cambiar en el cajón de navegación en la devolución de llamada cerrada. Su enfoque cambiará incorrectamente el título antes de que se cierre el cajón de navegación.

    – zyamys

    20 de febrero de 2014 a las 21:57


  • @zyamys Si viene con una sugerencia mejor, con mucho gusto la votaría. Esta fue la mejor respuesta que se me ocurrió en ese momento.

    – Warpzit

    21 de febrero de 2014 a las 7:48

  • Esto no funcionó para mí, así que lo cambié a getActivity().setTitle(R.string.thetitle); y luego funcionó.

    – simeg

    8 de marzo de 2015 a las 18:22

  • esto funciona solo si reemplaza el fragmento durante FragmentTransaction. ¿Qué sucede si agrega un fragmento en lugar de reemplazarlo?

    – Kamlesh

    7 de marzo de 2016 a las 8:49


Como la respuesta original es bastante antigua, esto también podría ser de ayuda. Como dice la documentación, es posible que desee registrar un listener para escuchar en el back stack los cambios en el hosting Activity:

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });

Luego, identifique la situación en el método de devolución de llamada y establezca un título adecuado, sin acceder al ActionBar desde el Fragment.

Esta es una solución más elegante ya que la Fragment no tiene que saber sobre el ActionBar existencia y Activity suele ser el lugar en el que se administra la pila trasera, por lo que parece ser más apropiado que se maneje allí. Fragment debe en todo momento ser considerada únicamente por su propio contenido, no por el entorno.

Más sobre el tema en el documentación.

  • La otra respuesta es antigua, pero todavía la considero la forma más simple y pragmática de garantizar que el título sea siempre correcto. Como mencioné en mi comentario, no necesita acceder directamente a ActionBar. La solución aquí requiere más código, pero tampoco ayuda para su fragmento inicial, que normalmente no se agrega a la pila posterior.

    – Christopher Orr

    23 de abril de 2014 a las 17:58

  • Además, no hay nada de malo en acceder a la barra de acción desde fragmentos. Google también proporciona API para cambiar elementos de menú dentro de fragmentos.

    – Warpzit

    24 de abril de 2014 a las 7:20

  • Después de un tiempo, llegué a la conclusión de que ambas soluciones están bien, solo depende de su caso de uso, a veces será más factible usar onResume y, a veces, un escucha del administrador de fragmentos. Entonces, en general, es bueno conocer sus herramientas, para que pueda aplicarlas adecuadamente a sus necesidades.

    – Maciej Pigulsky

    2 de julio de 2014 a las 15:25

  • @Maciej Pigulski ¿Y entonces? No sabemos nada sobre el contexto en esta devolución de llamada.

    – fralbo

    7 junio 2015 a las 16:17

  • @caBBAlainB, el contexto se mantiene en el FragmentManager de la actividad de alojamiento, por lo que tendría que determinar el título haciendo algunas comprobaciones engorrosas en el administrador; por lo tanto, no es excelente. Hoy no puedo recordar cuál fue mi razonamiento aquí. Creo que esta idea proviene principalmente de lo que está escrito en los documentos de Android vinculados en esta respuesta (párrafo sobre el fragmento del oyente). Hoy preferiría ir y usar la respuesta aceptada.

    – Maciej Pigulsky

    8 de junio de 2015 a las 9:37

Deje que la actividad de control haga todo el trabajo de la siguiente manera:

Escuche los eventos backstack (en onCreate() de actividad):

// Change the title back when the fragment is changed
    getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fragment = getFragment();
            setTitleFromFragment(fragment);
        }
    });

Obtenga el fragmento actual del contenedor:

/**
 * Returns the currently displayed fragment.
 * @return
 *      Fragment or null.
 */
private Fragment getFragment() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
    return fragment;
}

Establezca el fragmento dentro de la vista de contenido:

private void setFragment(Fragment fragment, boolean addToBackStack) {
    // Set the activity title
    setTitleFromFragment(fragment);
    .
    .
    .
}

  • Esto no es “dejar que la actividad haga todo el trabajo” si ha implementado alguna interfaz en cada uno Fragment de modo que setTitleFromFragment() puede recuperar su título. Ninguno de los cuales parece encajar en la parte “sin un montón de código repetitivo” de mi pregunta.

    – Christopher Orr

    09/10/2014 a las 20:36

  • No se menciona una interfaz en mi solución, su actividad puede verificar la instancia del fragmento y establecer el título en función de eso, lo que permite que su actividad haga todo el trabajo. Todos esos métodos se definen dentro de la actividad.

    – Hombre malo

    10 de octubre de 2014 a las 8:36

  • Sí, eso suena probablemente incluso peor desde el punto de vista del diseño/separación de preocupaciones/reutilización. Además, esto cambia de manera redundante el título dos veces para cada fragmento agregado a la pila posterior, ya que el método setTitleFromFragment se llama incondicionalmente en setFragment, ¿supongo? De todos modos, gracias, pero me quedaré con la respuesta pragmática y simple aceptada 🙂

    – Christopher Orr

    10 de octubre de 2014 a las 9:31

  • ¿Cómo suena esto “probablemente incluso peor desde el punto de vista del diseño/separación de preocupaciones/reutilización”? ¿Por favor respaldarlo con un argumento válido? Está permitiendo que la actividad sea un “controlador” y establezca su propio título en función de su contenido. ¿Qué pasa si quiero usar un fragmento en varios lugares o tener una actividad con varios fragmentos y todos intentan establecer el título?

    – Hombre malo

    13/10/2014 a las 10:45

  • Respuesta muy útil

    – VVB

    25 de enero de 2017 a las 6:56

Warpzit tiene razón. Esto también resuelve el problema del título cuando se cambia la orientación del dispositivo. Además, si usa el soporte v7 para la barra de acción, puede obtener la barra de acción de un fragmento como este:

@Override
public void onResume() {
    super.onResume();
    ((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}

Es mejor dejar que el sistema operativo haga la mayor parte del trabajo posible. Suponiendo que cada fragmento se nombra correctamente usando .addToBackStack(“título”), entonces puede anular onBackPressed algo como esto para lograr el comportamiento deseado:

// this example uses the AppCompat support library
// and works for dynamic fragment titles
@Override
public void onBackPressed() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    int count = fragmentManager.getBackStackEntryCount();
    if (count <= 1) {
        finish();
    }
    else {
        String title = fragmentManager.getBackStackEntryAt(count-2).getName();
        if (count == 2) {
            // here I am using a NavigationDrawer and open it when transitioning to the initial fragment
            // a second back-press will result in finish() being called above.
            mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
        }
        super.onBackPressed();
        Log.v(TAG, "onBackPressed - title="+title);
        getSupportActionBar().setTitle(title);
    }
}

avatar de usuario
Comunidad

Utilizo una solución similar al enfoque de Lee, pero reemplazando onBackStackChanged() método en su lugar.

Primero configuro el nombre del fragmento al agregar la transacción a la pila posterior.

getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_content, fragment)
                .addToBackStack(fragmentTitle)
                .commit();

Entonces anulo el onBackStackChanged() método y llamo setTitle() con el último nombre de entrada backstack.

@Override
public void onBackStackChanged() {
    int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
    FragmentManager.BackStackEntry lastBackStackEntry =
            getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);

    setTitle(lastBackStackEntry.getName());
}

avatar de usuario
Jurijs Turjanskis

Utilice el método Fragmentos:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

Se llama en cada aparición de Fragment, pero onResume no.

  • sí … ninguna de las respuestas no se guarda mi problema … esto funciona bien. Puede que no sea de manera profesional… pero es un poco raro.

    – Ranjithkumar

    30 de abril de 2016 a las 14:28

  • @Bevor para que esto funcione, necesita un menú de opciones, puede activarlo para un fragmento llamando a “setHasOptionsMenu(true);” en el método OnCreateView(…) de su fragmento.

    – Dwagner

    20 de marzo de 2019 a las 18:32

¿Ha sido útil esta solución?