prendi l'ultimo frammento in backstack


104

Come posso ottenere l'ultima istanza di frammento aggiunta nel backstack (se non conosco il tag e l'id del frammento)?

FragmentManager fragManager = activity.getSupportFragmentManager();
FragmentTransaction fragTransacion = fragMgr.beginTransaction();

/****After add , replace fragments 
  (some of the fragments are add to backstack , some are not)***/

//HERE, How can I get the latest added fragment from backstack ??

Risposte:


156

Puoi utilizzare il getName()metodo di FragmentManager.BackStackEntrycui è stato introdotto nel livello API 14. Questo metodo restituirà un tag che era quello che hai usato quando hai aggiunto il frammento al backstack con addTobackStack(tag).

int index = getActivity().getFragmentManager().getBackStackEntryCount() - 1
FragmentManager.BackStackEntry backEntry = getFragmentManager().getBackStackEntryAt(index);
String tag = backEntry.getName();
Fragment fragment = getFragmentManager().findFragmentByTag(tag);

Devi assicurarti di aver aggiunto il frammento al backstack in questo modo:

fragmentTransaction.addToBackStack(tag);

24
per trovare frammento per tag deve essere aggiunto / sostituito con lo stesso tag. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)o FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib

3
Il problema è che se hai un frammento nello stack posteriore due volte, non puoi recuperare un frammento specifico quando tutto ciò che hai è l'entità index o backstackentry.
Justin

14
Qual è il punto di chiamarlo a stackse non riesco ad accedere al frammento tramite un metodo pop?
Kenny Worden


1
Non so perché, ma findFragmentByTag restituisce null, anche quando il debugger mostra chiaramente che il tag è ok.
htafoya

49
FragmentManager.findFragmentById(fragmentsContainerId) 

la funzione restituisce il collegamento all'inizio Fragmentin backstack. Esempio di utilizzo:

    fragmentManager.addOnBackStackChangedListener(new OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fr = fragmentManager.findFragmentById(R.id.fragmentsContainer);
            if(fr!=null){
                Log.e("fragment=", fr.getClass().getSimpleName());
            }
        }
    });

2
Questa risposta funziona bene nel mio progetto poiché aggiungo ogni frammento tranne il frammento radice allo stack posteriore. Ma immagino che questa risposta non funzionerà se l'ultimo frammento aggiunto non è stato aggiunto al backstack.
Arne Evertsson

40

Personalmente ho provato molte di queste soluzioni e ho trovato questa soluzione funzionante:

Aggiungi questo metodo di utilità che verrà utilizzato più volte di seguito per ottenere il numero di frammenti nel tuo backstack:

protected int getFragmentCount() {
    return getSupportFragmentManager().getBackStackEntryCount();
}

Quindi, quando aggiungi / sostituisci il tuo frammento utilizzando il metodo FragmentTransaction, genera un tag univoco al tuo frammento (ad esempio: utilizzando il numero di frammenti nel tuo stack):

getSupportFragmentManager().beginTransaction().add(yourContainerId, yourFragment, Integer.toString(getFragmentCount()));

Infine, puoi trovare uno qualsiasi dei tuoi frammenti nel tuo backstack con questo metodo:

private Fragment getFragmentAt(int index) {
    return getFragmentCount() > 0 ? getSupportFragmentManager().findFragmentByTag(Integer.toString(index)) : null;
}

Pertanto, il recupero del frammento superiore nel tuo backstack può essere facilmente ottenuto chiamando:

protected Fragment getCurrentFragment() {
    return getFragmentAt(getFragmentCount() - 1);
}

Spero che questo ti aiuti!


10

questo metodo di supporto ottiene il frammento dalla cima dello stack:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}

1
Grazie. La risposta più elegante. Aggiunto qui per gli amanti Kotlin stackoverflow.com/a/47336504/1761406~~V~~3rd
Shirane85

7

Kotlin

activity.supportFragmentManager.fragments.last()

4
Potrebbe essere necessario utilizzare .lastOrNull()nel caso in cui fragmentssia vuoto.
Westy92

6

C'è un elenco di frammenti nel fragmentMananger. Tieni presente che la rimozione di un frammento non riduce la dimensione dell'elenco (la voce del frammento diventa nulla). Pertanto, una valida soluzione sarebbe:

public Fragment getTopFragment() {
 List<Fragment> fragentList = fragmentManager.getFragments();
 Fragment top = null;
  for (int i = fragentList.size() -1; i>=0 ; i--) {
   top = (Fragment) fragentList.get(i);
     if (top != null) {
       return top;
     }
   }
 return top;
}

2
Non affidabile. Tre ragioni: 1) Questo è un elenco di tutti i frammenti di cui è a conoscenza il gestore dei frammenti, non solo quelli nello stack. 2) Non vi è alcuna garanzia che il gestore dei frammenti continuerà ad aggiungerne di nuovi alla fine dell'elenco. Certo, lo farà in semplici test, ma cosa succede se un frammento viene rimosso, lasciando un valore nullo, e quindi in alcune circostanze il gestore dei frammenti decide di riutilizzare quello slot vuoto? Non dico che lo faccia, ma non c'è garanzia che non lo farà mai, in nessuna circostanza. 3) Se tu o qualche futuro programmatore iniziate a utilizzare attach / detach per gestire i frammenti, questo non corrisponderà allo stack.
ToolmakerSteve

Ciao, grazie per la tua soluzione ma non è bella e facile
Muhammad Ali

Dubito che sia una soluzione funzionante. getFragments()restituisce una raccolta ridotta di frammenti. Forse l'ultimo è visibile, ma non molti altri.
CoolMind

5

La risposta data da deepak goel non funziona per me perché ottengo sempre nulla da entry.getName();

Quello che faccio è impostare un tag per il frammento in questo modo:

ft.add(R.id.fragment_container, fragmentIn, FRAGMENT_TAG);

Dove ft è la mia transazione di frammento ed FRAGMENT_TAGè il tag. Quindi uso questo codice per ottenere il frammento:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);

Funzionerà solo se si assegna a tutto il frammento lo stesso tag, il che non è una buona idea se si desidera trovare un frammento specifico in seguito.
Shirane85

4

Ho appena preso la risposta (corretta) di @roghayeh hosseini e ce l'ho fatta a Kotlin per quelli qui nel 2017 :)

fun getTopFragment(): Fragment? {
    supportFragmentManager.run {
        return when (backStackEntryCount) {
            0 -> null
            else -> findFragmentByTag(getBackStackEntryAt(backStackEntryCount - 1).name)
        }
    }
}

* Dovrebbe essere chiamato dall'interno di un'attività.

Godere :)


3

puoi usare getBackStackEntryAt () . Per sapere quante voci contiene l'attività nel backstack puoi usare getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;

11
Ma come posso ottenere l'ultimo frammento nel backstack ?? PopBackStackEntryAt () restituisce solo un'istanza BackStackEntry, NON frammento
Leem.fin

sì, hai ragione, ma ogni BackStackEntry contiene un ID che puoi recuperare con getId (). puoi usare questo ID per recuperare il frammento
Blackbelt

1
Per ottenere l'ultimo frammento: getBackStackEntryCount () - 1
An-droid

3
Questa risposta è sbagliata, vedo che le voci del backstack hanno un id di 0, quindi non posso recuperare il frammento per id.
Justin,

1
Questo è vecchio, ma per chiunque vada qui: il back stack non contiene frammenti, ma transazioni di frammenti, ecco perché questa risposta è sbagliata
maciekjanusz

3

Aggiungerò qualcosa alla risposta di Deepak Goel poiché molte persone, me compreso, stavano ottenendo un valore nullo usando il suo metodo. Apparentemente per far funzionare il tag quando aggiungi un frammento al backstack dovresti farlo in questo modo:

getSupportFragmentManager.beginTransaction().replace(R.id.container_id,FragmentName,TAG_NAME).addToBackStack(TAG_NAME).commit();

Devi aggiungere lo stesso tag due volte.

Avrei commentato ma non ho 50 reputazione.


ne hai 50 ora :)
davidbilla

2

Tenere la schiena proprio stack: myBackStack. Come Addframmento di FragmentManager, aggiungilo anche a myBackStack. In onBackStackChanged()pop da myBackStackquando la sua lunghezza è maggiore di getBackStackEntryCount.


2

Sembra che qualcosa sia cambiato in meglio, perché il codice seguente funziona perfettamente per me, ma non l'ho trovato nelle risposte già fornite.

Kotlin:

supportFragmentManager.fragments[supportFragmentManager.fragments.size - 1]

Giava:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)

2

Se usi addToBackStack(), puoi usare il seguente codice.

List<Fragment> fragments = fragmentManager.getFragments(); activeFragment = fragments.get(fragments.size() - 1);


1

In realtà non c'è l'ultimo frammento aggiunto allo stack perché puoi aggiungere più o frammenti allo stack in una singola transazione o semplicemente rimuovere i frammenti senza aggiungerne uno nuovo.

Se vuoi davvero avere una pila di frammenti ed essere in grado di accedere a un frammento tramite il suo indice nella pila, faresti meglio ad avere un livello di astrazione sopra FragmentManagere sul suo backstack. Ecco come puoi farlo:

public class FragmentStackManager {
  private final FragmentManager fragmentManager;
  private final int containerId;

  private final List<Fragment> fragments = new ArrayList<>();

  public FragmentStackManager(final FragmentManager fragmentManager,
      final int containerId) {
    this.fragmentManager = fragmentManager;
    this.containerId = containerId;
  }

  public Parcelable saveState() {
    final Bundle state = new Bundle(fragments.size());
    for (int i = 0, count = fragments.size(); i < count; ++i) {
      fragmentManager.putFragment(state, Integer.toString(i), fragments.get(i));
    }
    return state;
  }

  public void restoreState(final Parcelable state) {
    if (state instanceof Bundle) {
      final Bundle bundle = (Bundle) state;
      int index = 0;
      while (true) {
        final Fragment fragment =
            fragmentManager.getFragment(bundle, Integer.toString(index));
        if (fragment == null) {
          break;
        }

        fragments.add(fragment);
        index += 1;
      }
    }
  }

  public void replace(final Fragment fragment) {
    fragmentManager.popBackStackImmediate(
        null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    fragmentManager.beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.clear();
    fragments.add(fragment);
  }

  public void push(final Fragment fragment) {
    fragmentManager
        .beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.add(fragment);
  }

  public boolean pop() {
    if (isEmpty()) {
      return false;
    }

    fragmentManager.popBackStackImmediate();

    fragments.remove(fragments.size() - 1);
    return true;
  }

  public boolean isEmpty() {
    return fragments.isEmpty();
  }

  public int size() {
    return fragments.size();
  }

  public Fragment getFragment(final int index) {
    return fragments.get(index);
  }
}

Ora, invece di aggiungere e rimuovere i frammenti chiamando FragmentManagerdirettamente, si dovrebbe usare push(), replace()e pop()metodi di FragmentStackManager. E sarai in grado di accedere al frammento più in alto semplicemente chiamando stack.get(stack.size() - 1).

Ma se ti piacciono gli hack, devo usare altri modi per fare cose simili. L'unica cosa che devo menzionare è che questi hack funzioneranno solo con frammenti di supporto.

Il primo trucco consiste nell'aggiungere tutti i frammenti attivi al gestore dei frammenti. Se sostituisci i frammenti uno per uno e li estrai dallo stack, questo metodo restituirà il frammento più in alto:

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    final List<Fragment> fragments = fragmentManager.getFragments();
    final List<Fragment> topFragments = new ArrayList<>();

    for (final Fragment fragment : fragments) {
      if (fragment != null && fragment.isResumed()) {
        topFragments.add(fragment);
      }
    }

    return topFragments;
  }
}

Il secondo approccio è l'evento più hacky e consente di ottenere tutti i frammenti aggiunti nell'ultima transazione per la quale addToBackStackè stato chiamato:

package android.support.v4.app;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    if (fragmentManager.getBackStackEntryCount() == 0) {
      return Collections.emptyList();
    }

    final List<Fragment> fragments = new ArrayList<>();

    final int count = fragmentManager.getBackStackEntryCount();
    final BackStackRecord record =
        (BackStackRecord) fragmentManager.getBackStackEntryAt(count - 1);
    BackStackRecord.Op op = record.mHead;
    while (op != null) {
      switch (op.cmd) {
        case BackStackRecord.OP_ADD:
        case BackStackRecord.OP_REPLACE:
        case BackStackRecord.OP_SHOW:
        case BackStackRecord.OP_ATTACH:
          fragments.add(op.fragment);
      }
      op = op.next;
    }

    return fragments;
  }
}

Si noti che in questo caso è necessario inserire questa classe in android.support.v4.apppackage.


0

Oppure puoi semplicemente aggiungere un tag quando aggiungi frammenti corrispondenti al loro contenuto e utilizzare un semplice campo String statico (puoi anche salvarlo nel bundle di istanze di attività nel metodo onSaveInstanceState (Bundle)) per conservare l'ultimo tag di frammento aggiunto e ottenere questo frammento daTag () in qualsiasi momento tu abbia bisogno ...


0

La risposta più alta (Deepak Goel) non ha funzionato bene per me. In qualche modo il tag non è stato aggiunto correttamente.

Ho finito per inviare l'ID del frammento attraverso il flusso (usando gli intenti) e recuperarlo direttamente dal gestore dei frammenti.

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.