¿Cómo hacer que un Fragment se elimine, es decir, su equivalente de finish ()?

230

Estoy convirtiendo una aplicación para usar fragmentos usando la biblioteca de compatibilidad. Ahora actualmente tengo una serie de actividades (ABCD) que se encadenan entre sí, D tiene un botón 'OK' que, cuando se presionan las llamadas, finaliza y luego emerge onActivityResult()para destruir adicionalmente C y B.

Para mi versión previa del fragmento Honycomb, cada actividad es efectivamente un contenedor de fragmentos Af Bf Cf Df. Todas las actividades se inician a través de startActivityForResult()y onActivityResult()dentro de cada uno de los fragmentos pueden llamar felizmentegetActivity().finish()

Sin embargo, el problema que tengo es que en mi versión Honeycomb solo tengo una actividad, A, y los fragmentos Bf, Cf, Df se cargan usando el FragmentManager.

Lo que no entiendo es qué hacer en Df cuando se presiona 'OK' para eliminar los fragmentos Df, Cf y Bf.

Intenté que el fragmento saliera de la pila, pero esto resultó en una excepción. onActivityResult()es inútil porque no he cargado el fragmento usando startActivityForResult().

¿Estoy pensando en esto completamente de la manera incorrecta? ¿Debo implementar algún tipo de escucha que se comunique con el fragmento o la actividad principal para hacer el pop usando el administrador de transacciones?

PJL
fuente
77
¿qué pasa con ((YourActivity) getActivity ()). onBackPressed ();
Viswanath Lekshmanan
@ViswanathLekshmanan la respuesta de tu comentario es útil para mí .. 1 Voto de mi parte
Abhishek Singh
@AbhishekSingh Es bueno escuchar :)
Viswanath Lekshmanan
@ViswanathLekshmanan :)
Abhishek Singh

Respuestas:

62

Lo que no entiendo es qué hacer en Df cuando se presiona 'OK' para eliminar los fragmentos Df, Cf y Bf.

Paso # 1: ¡Df dile a D "yo! ¡Tenemos el clic OK!" llamando a un método, ya sea en la actividad en sí o en una instancia de interfaz proporcionada por la actividad.

Paso # 2: Haz que D elimine los fragmentos a través de FragmentManager.

La actividad de alojamiento (D) es la que sabe qué otros fragmentos hay en la actividad (frente a estar en otras actividades). Por lo tanto, los eventos en fragmentos que pueden afectar la mezcla de fragmentos deben propagarse a la actividad, lo que hará los movimientos de orquestación apropiados.

CommonsWare
fuente
Pero en mi versión Honeycomb no hay D, esa es mi dificultad. Simplemente hay una actividad A que carga el fragmento Bf, que carga Cf, que carga Df usando FragmentTransaction.
PJL
1
@PJL: Lo siento, quise decir A. Esta es una razón para usar una interfaz de escucha, por lo que múltiples actividades pueden responder al evento "obtuvimos el clic OK" de Df.
CommonsWare
1
Como actualmente estoy portando, llamé a un método de escucha desde los fragmentos del método onActivityResult de Df a la actividad, luego llamé popBackStack en el FragmentManager. Sin embargo, esto da como resultado una excepción "IllegalStateException: no se puede realizar esta acción después de onSaveInstanceState '. ¿Alguna idea de cómo puedo superar esto?"
PJL,
1
@DiegoPalomar: finish()debería ser suficiente.
CommonsWare
1
@ user3364963: Ha pasado un tiempo desde que investigué eso, pero IIRC, se destruye cuando se saca de la pila posterior. Agregue un onDestroy()método a su fragmento y vea si se llama.
CommonsWare
313

Si bien podría no ser el mejor enfoque, el equivalente más cercano que puedo pensar que funciona es esto con la biblioteca de compatibilidad / soporte

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();

o

getActivity().getFragmentManager().beginTransaction().remove(this).commit();

de otra manera.

Además, puede usar el backstack y abrirlo. Sin embargo, tenga en cuenta que el fragmento podría no estar en el backstack (dependiendo de la transacción del fragmento que lo llevó allí ...) o podría no ser el último que entró en la pila, por lo que al extraer la pila podría eliminar el incorrecto ...

Manfred Moser
fuente
11
Si bien este enfoque funciona, si está usando addToBackStack (nulo) dejará el controlador del botón Atrás +1. Entonces tendrás que presionarlo dos veces.
user123321
34
"pop" el fragmento del FragmentManager.
user123321
2
He intentado el procedimiento anterior pero está dando este error "java-lang-illegalstateexception-can-not-perform-this-action-after-on-saveinstance". Entonces, ¿dónde exactamente tengo que eliminar el fragmento
KK_07k11A0585
66
Esta respuesta es una mala práctica y no debería estar obteniendo votos. Los fragmentos no están destinados a ser conscientes de sí mismos de esta manera. Mata la reutilización, que es el punto de fragmentos! El fragmento debe indicar la actividad para eliminarlo a través de cualquier cantidad de medios. El método de interfaz de devolución de llamada es una opción popular. developer.android.com/training/basics/fragments/…
colintheshots
2
@ManfredMoser No estoy de acuerdo. Este es en gran medida el punto de la pregunta. Tiene una secuencia completa de fragmentos para eliminar. Este código no tiene verificaciones nulas o verifica si la actividad está adjunta. Se interrumpirá la producción porque depende de demasiadas cosas que un fragmento no sabe.
colintheshots
263

Puede usar el siguiente enfoque, funciona bien:

getActivity().getSupportFragmentManager().popBackStack();
Eric Yuan
fuente
44
Esta respuesta es como 10 veces mejor que la aceptada, directamente al grano.
nikib3ro
50
También es 10 veces peor con respecto al diseño que el aceptado. Se supone que un fragmento es un pequeño "ayudante" para una actividad y nunca debe tener control sobre sí mismo u otros fragmentos
Patrick
66
La solución no es correcta, como señaló @avalancha. Echa un vistazo a developer.android.com/guide/components/…
the_dark_destructor el
2
Estoy usando este método onActivityResult y obtengo el error "No se puede realizar esta acción después de onSaveInstanceState". ¿Cómo puedo resolverlo?
Jayeshkumar Sojitra
1
popBackStack()es la única solución que funciona cuando desea volver a establecer el título de la barra de acción al estado anterior después de eliminar el fragmento. De lo contrario, no necesitaría combinar las dos soluciones de alta calificación de stackoverflow.com/questions/13472258/… y esta solución aquí para establecer siempre el título de barra de acción adecuado después de varios casos de uso. como volver a presionar, eliminar, agregar reemplazar, etc.
Bevor
36

Debes dejar que la actividad se ocupe de agregar y quitar fragmentos, como dice CommonsWare, usa un oyente. Aquí hay un ejemplo:

public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {

    // onCreate etc

    @Override
    public void onFragmentSuicide(String tag) {
        // Check tag if you do this with more than one fragmen, then:
        getSupportFragmentManager().popBackStack();
    }
}

public interface SuicidalFragmentListener {
    void onFragmentSuicide(String tag);
}

public class MyFragment extends Fragment {

    // onCreateView etc

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
           suicideListener = (SuicidalFragmentListener) activity;
        } catch (ClassCastException e) {
           throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // Attach the close listener to whatever action on the fragment you want
        addSuicideTouchListener();
    }

    private void addSuicideTouchListener() {
        getView().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              suicideListener.onFragmentSuicide(getTag());
            }
        });
    }
}
Blundell
fuente
55
Suicidio mucho estilo emo? ¿Qué tal "SelfClosing" o "AutoClose" o "SmartClose" (r)
DritanX
21
no se cierra es MORIR PARA SIEMPRE ;-(
Blundell
3
Este es un enfoque mucho más limpio que las otras respuestas. La actividad crea y presenta el fragmento, y debe controlar su ciclo de vida. Cuando sucede algo que indica que el fragmento ya no debería estar a la vista, debería indicarle a la Actividad eso y dejar que la actividad lo elimine.
Christopher Pickslay
77
Técnicamente, si se supone que la actividad mata al fragmento, entonces el fragmento no es suicida. La actividad es homicida.
Casey Murray
17

En la Actividad / AppCompatActivity:

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        // if you want to handle DrawerLayout
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            super.onBackPressed();
        } else {
            getFragmentManager().popBackStack();
        }
    }
}

y luego llame al fragmento:

getActivity().onBackPressed();

o como se indica en otras respuestas, llame a esto en el fragmento:

getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
Codeversed
fuente
5

Si está utilizando el nuevo componente de navegación, es simple como

findNavController().popBackStack()

Hará toda la FragmentTransaction detrás de usted.

Gastón Saillén
fuente
4

Vea si sus necesidades son satisfechas por un DialogFragment. DialogFragment tiene un método de despido (). Mucho más limpio en mi opinión.

Vasudev
fuente
3

Creo un método simple para eso

popBackStack(getSupportFragmentManager());

Que colocarlo en mi clase ActivityUtils

public static void popBackStack(FragmentManager manager){
        FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
        manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

Es un gran trabajo, diviértete!

Eliasz Kubala
fuente
2
No entiendo el propósito de tu método. El popBackStack original parece completamente adecuado.
El increíble Jan
1

OnCreate:

//Add comment fragment
            container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
            mPictureFragment = new fmtAttachPicture();

            var trans = SupportFragmentManager.BeginTransaction();
            trans.Add(container.Id, mPictureFragment, "fmtPicture");
            trans.Show(mPictureFragment); trans.Commit();

Así es como oculto el fragmento en el evento de clic 1

//Close fragment
    var trans = SupportFragmentManager.BeginTransaction();
    trans.Hide(mPictureFragment);
    trans.AddToBackStack(null);
    trans.Commit();

Luego lo muestra de vuelta int evento 2

var trans = SupportFragmentManager.BeginTransaction();
            trans.Show(mPictureFragment); trans.Commit();
CodeSi
fuente
1

Si necesita retroceder desde el cuarto fragmento en el historial de backstack hasta el primero, ¡use etiquetas!

Cuando agrega el primer fragmento, debe usar algo como esto:

getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit() 

o

getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()

Y cuando quieres mostrar los Fragmentos B, C y D, usas esto:

getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()

y otras letras ...

Para volver a FragmentA, simplemente llame popBackStack(0, "A"), sí, use el indicador que especificó cuando lo agregó, y tenga en cuenta que debe ser el mismo indicador en el comando addToBackStack(), no el que se usa en el comando reemplazar o agregar.

De nada ;)

Diego Ramírez Vásquez
fuente
He probado 'popBackStack (0, "A")' y mi aplicación vuelve al fragmento A, pero solo quiero que ese fragmento se elimine de Back Stack ... ¿Cómo puedo eliminar el fragmento de Stack sin mostrarlo en la pantalla?
KryNaC
1

Para cerrar un fragmento dentro del mismo fragmento

getActivity().onBackPressed();
Dino Sunny
fuente
-9

¿Por qué no solo:

getActivity (). finish ();

Será
fuente
44
Esto finalizará toda la actividad, no solo el fragmento.
Murtadha S.