Come ottenere un frammento per rimuovere se stesso, ovvero l'equivalente di finish ()?


230

Sto convertendo un'app per usare i frammenti usando la libreria di compatibilità. Ora al momento ho un certo numero di attività (ABCD) che si incatenano l'una sull'altra, D ha un pulsante 'OK' che quando preme le chiamate finiscono che poi si gonfiano onActivityResult()per distruggere ulteriormente C e B.

Per la mia versione di frammento pre Honycomb ogni attività è effettivamente un wrapper su frammenti Af Bf Cf Df. Tutte le attività vengono avviate tramite startActivityForResult()e onActivityResult()all'interno di ciascuno dei frammenti che possono essere chiamate felicementegetActivity().finish()

Il problema che sto riscontrando è che nella mia versione Honeycomb ho solo un'attività, A, e i frammenti Bf, Cf, Df vengono caricati usando FragmentManager.

Quello che non capisco è cosa fare in Df quando si preme 'OK' per rimuovere i frammenti Df, Cf e Bf?

Ho provato a far schioccare il frammento dallo stack ma questo ha provocato un'eccezione. onActivityResult()è inutile perché non ho caricato il frammento usando startActivityForResult().

Sto pensando a questo completamente nel modo sbagliato? Dovrei implementare una sorta di listener che comunica con il frammento o l'attività padre per fare il pop usando il gestore delle transazioni?


7
che dire di ((YourActivity) getActivity ()). onBackPressed ();
Viswanath Lekshmanan,

@ViswanathLekshmanan la tua risposta al commento è utile per me .. 1 Voto positivo da parte mia
Abhishek Singh,

@AbhishekSingh Buono a sapersi :)
Viswanath Lekshmanan,

@ViswanathLekshmanan :)
Abhishek Singh,

Risposte:


62

Quello che non capisco è cosa fare in Df quando si preme 'OK' per rimuovere i frammenti Df, Cf e Bf?

Step # 1: Chiedi a Df di dire "yo! Abbiamo ottenuto il clic OK!" chiamando un metodo, sull'attività stessa o su un'istanza di interfaccia fornita dall'attività.

Step # 2: fai rimuovere a D i frammenti FragmentManager.

L'attività di hosting (D) è quella che sa quali altri frammenti sono presenti nell'attività (anziché essere in altre attività). Quindi, gli eventi in frammenti che potrebbero influenzare il mix di frammenti dovrebbero essere propagati all'attività, che farà le mosse dell'orchestrazione appropriate.


Ma nella mia versione Honeycomb non c'è la D, questa è la mia difficile. Esiste semplicemente una attività A che carica il frammento Bf, che carica Cf, che carica Df usando FragmentTransaction.
PJL

1
@PJL: Siamo spiacenti, intendevo A. Questo è uno dei motivi per utilizzare un'interfaccia listener, quindi più attività possono rispondere all'evento "abbiamo ottenuto il clic OK" da Df.
CommonsWare

1
Dato che attualmente sto eseguendo il porting, ho chiamato un metodo listener dai frammenti del metodo onActivityResult di Df all'attività in seguito alla quale ho quindi chiamato popBackStack sul FragmentManager. Tuttavia, questo si traduce in un'eccezione "IllegalStateException: Impossibile eseguire questa azione dopo onSaveInstanceState '. Qualche idea su come posso superare questo?
PJL

1
@DiegoPalomar: finish()dovrebbe bastare.
CommonsWare,

1
@ user3364963: È passato un po 'di tempo da quando l'ho studiato, ma IIRC, viene distrutto quando viene estratto dallo stack posteriore. Aggiungi un onDestroy()metodo al tuo frammento e vedi se viene chiamato.
CommonsWare,

313

Anche se potrebbe non essere l'approccio migliore, l'equivalente più vicino a cui riesco a pensare che funzioni è questo con la libreria di supporto / compatibilità

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

o

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

altrimenti.

Inoltre è possibile utilizzare lo zaino e pop-up. Tuttavia, tieni presente che il frammento potrebbe non essere sul backstack (a seconda della transazione del frammento che lo ha portato lì ..) o potrebbe non essere l'ultimo che è entrato nello stack in modo che lo scoppio dello stack potrebbe rimuovere quello sbagliato ...


11
Mentre questo approccio funziona, se stai usando addToBackStack (null) lascerà il gestore del pulsante Indietro +1. Quindi dovrai premerlo due volte.
user123321

34
"pop" il frammento dal FragmentManager.
user123321

2
Ho provato la procedura sopra ma sta dando questo errore "java-lang-illegalstateexception-can-non-perform-this-action-after-onsaveinstance". Quindi, dove esattamente devo rimuovere il frammento
KK_07k11A0585,

6
Questa risposta è una cattiva pratica e non dovrebbe alzarsi di voti. I frammenti non sono pensati per essere autocoscienti in questo modo. Uccide la riusabilità, che è il punto di frammenti! Il frammento dovrebbe segnalare l'attività per rimuoverlo con qualsiasi numero di mezzi. Il metodo dell'interfaccia di callback è una scelta popolare. developer.android.com/training/basics/fragments/…
colintheshots

2
@ManfredMoser Non sono d'accordo. Questo è molto il punto della domanda. Ha un'intera sequenza di frammenti da rimuovere. Questo codice non ha controlli nulli o controlli per verificare se l'attività è collegata. Si interromperà nella produzione perché dipende da troppe cose che un frammento non conosce.
colintheshots

263

Puoi usare l'approccio seguente, funziona benissimo:

getActivity().getSupportFragmentManager().popBackStack();

44
Questa risposta è circa 10 volte migliore di quella accettata, direttamente al punto.
nikib3ro,

50
È anche 10 volte peggiore in termini di design rispetto a quello accettato. Un frammento dovrebbe essere un piccolo "aiutante" di un'attività e non dovrebbe mai avere il controllo di se stesso o di altri frammenti
Patrick

6
La soluzione non è corretta come sottolineato da @avalancha.
Dai

2
Sto usando questo metodo onActivityResult e ricevo l'errore "Impossibile eseguire questa azione dopo onSaveInstanceState". Come posso risolverlo?
Jayeshkumar Sojitra,

1
popBackStack()è l'unica soluzione che funziona quando si desidera riportare il titolo della barra delle azioni allo stato precedente dopo aver eliminato il frammento. Altrimenti non avrei bisogno di combinare entrambe le soluzioni di alto livello da stackoverflow.com/questions/13472258/… e questa soluzione qui per impostare sempre il titolo corretto della barra delle azioni dopo vari casi d'uso. come premere indietro, eliminare, aggiungere sostituendo e così via.
Bevor

36

Dovresti permettere all'Attività di aggiungere e rimuovere frammenti, come dice CommonsWare, usare un ascoltatore. Ecco un esempio:

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());
            }
        });
    }
}

5
Il suicidio è molto emo? Che ne dici di "SelfClosing" o "AutoClose" o "SmartClose" (r)
DritanX

21
non sta chiudendo, sta morendo per sempre ;-(
Blundell,

3
Questo è un approccio molto più pulito rispetto alle altre risposte. L'attività crea e presenta il frammento e dovrebbe controllarne il ciclo di vita. Quando succede qualcosa che indica che il frammento non dovrebbe più essere visibile, dovrebbe dirlo all'Attività e lasciare che l'attività lo rimuova.
Christopher Pickslay,

7
Tecnicamente, se dobbiamo supporre che l'attività uccida il frammento, allora il frammento non è suicida. L'attività è omicida.
Casey Murray,

17

In Activity / 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();
        }
    }
}

e quindi chiama nel frammento:

getActivity().onBackPressed();

o come indicato in altre risposte, chiamalo nel frammento:

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

5

Se si utilizza il nuovo componente di navigazione, è semplice come

findNavController().popBackStack()

Farà tutta la FragmentTransaction dietro di te.


4

Verifica se i tuoi bisogni sono soddisfatti da un DialogFragment. DialogFragment ha un metodo dismiss (). Molto più pulito secondo me.


3

Creo un metodo semplice per quello

popBackStack(getSupportFragmentManager());

Quindi inseriscilo nella mia classe ActivityUtils

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

Funziona benissimo, divertiti!


2
Non capisco lo scopo del tuo metodo. Il popBackStack originale sembra completamente adeguato.
L'incredibile gennaio

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();

Ecco come nascondo il frammento nell'evento click 1

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

Quindi lo mostra all'evento 2

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

1

Se è necessario eseguire il pop-up dal quarto frammento della cronologia del backstack al primo, utilizzare i tag !!!

Quando aggiungi il primo frammento dovresti usare qualcosa del genere:

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

o

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

E quando vuoi mostrare i frammenti B, C e D usi questo:

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

e altre lettere ....

Per tornare ad FragmentA, basta chiamare popBackStack(0, "A"), sì, utilizzare il flag specificato quando lo si aggiunge e notare che deve essere lo stesso flag nel comando addToBackStack(), non quello utilizzato nel comando di sostituzione o aggiunta.

Prego ;)


Ho testato 'popBackStack (0, "A")' e la mia app torna al frammento A, ma voglio solo che quel frammento venga rimosso dallo Stack posteriore ... Come posso rimuovere il frammento dallo Stack senza mostrarlo sullo schermo ??
KryNaC,

1

Per chiudere un frammento all'interno dello stesso frammento

getActivity().onBackPressed();

-9

Perché non solo:

getActivity () finitura ().;


4
Questo finirà l'intera attività non solo il frammento.
Murtadha S.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.