Holen Sie sich das neueste Fragment im Backstack

104

Wie kann ich die neueste Fragmentinstanz im Backstack hinzufügen lassen (wenn ich das Fragment-Tag und die ID nicht kenne)?

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 ??
Leem.fin
quelle

Antworten:

156

Sie können die getName()Methode verwenden, FragmentManager.BackStackEntrydie in API-Level 14 eingeführt wurde. Diese Methode gibt ein Tag zurück, das Sie verwendet haben, als Sie das Fragment zum Backstack mit hinzugefügt haben addTobackStack(tag).

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

Sie müssen sicherstellen, dass Sie das Fragment wie folgt zum Backstack hinzugefügt haben:

fragmentTransaction.addToBackStack(tag);
Deepak Goel
quelle
24
Um ein Fragment nach Tag zu finden, muss es hinzugefügt / durch dasselbe Tag ersetzt werden. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)oder FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib
3
Das Problem dabei ist, dass Sie ein bestimmtes Fragment nicht abrufen können, wenn Sie zweimal ein Fragment im Backstack haben, wenn Sie nur den Index oder die Backstackentry-Entität haben.
Justin
14
Was bringt es, stackwenn ich es nicht über eine Pop-Methode auf das Fragment zugreifen kann?
Kenny Worden
1
Ich weiß nicht warum, aber findFragmentByTag gibt null zurück, selbst wenn der Debugger deutlich zeigt, dass das Tag in Ordnung ist.
Htafoya
49
FragmentManager.findFragmentById(fragmentsContainerId) 

Die Funktion gibt den Link Fragmentim Backstack nach oben zurück. Anwendungsbeispiel:

    fragmentManager.addOnBackStackChangedListener(new OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fr = fragmentManager.findFragmentById(R.id.fragmentsContainer);
            if(fr!=null){
                Log.e("fragment=", fr.getClass().getSimpleName());
            }
        }
    });
Ashakirov
quelle
2
Diese Antwort funktioniert in meinem Projekt gut, da ich jedes Fragment außer dem Stammfragment zum Backstack hinzufüge. Aber ich denke, diese Antwort wird nicht funktionieren, wenn das zuletzt hinzugefügte Fragment nicht zum Backstack hinzugefügt wurde.
Arne Evertsson
40

Ich habe persönlich viele dieser Lösungen ausprobiert und am Ende diese funktionierende Lösung gefunden:

Fügen Sie diese Dienstprogrammmethode hinzu, die im Folgenden mehrmals verwendet wird, um die Anzahl der Fragmente in Ihrem Backstack zu ermitteln:

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

Wenn Sie dann Ihr Fragment mithilfe der FragmentTransaction-Methode hinzufügen / ersetzen, generieren Sie ein eindeutiges Tag für Ihr Fragment (z. B. mithilfe der Anzahl der Fragmente in Ihrem Stapel):

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

Schließlich können Sie mit dieser Methode eines Ihrer Fragmente in Ihrem Backstack finden:

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

Daher können Sie das oberste Fragment in Ihrem Backstack leicht abrufen, indem Sie Folgendes aufrufen:

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

Hoffe das hilft!

ptitvinou
quelle
10

Diese Hilfsmethode ruft ein Fragment vom oberen Rand des Stapels ab:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
roghayeh hosseini
quelle
1
Vielen Dank. Die eleganteste Antwort. Hier für Kotlin-Liebhaber hinzugefügt stackoverflow.com/a/47336504/1761406
Shirane85
7

Kotlin

activity.supportFragmentManager.fragments.last()
Der beste Künstler
quelle
4
Möglicherweise müssen Sie verwenden, .lastOrNull()falls fragmentsleer ist.
Westy92
6

Der fragmentMananger enthält eine Liste von Fragmenten. Beachten Sie, dass durch das Entfernen eines Fragments die Listengröße nicht verringert wird (der Fragmenteintrag wird einfach auf null gesetzt). Eine gültige Lösung wäre daher:

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;
}
Erez
quelle
2
Nicht zuverlässig. Drei Gründe: 1) Dies ist eine Liste aller Fragmente, die dem Fragmentmanager bekannt sind, nicht nur der auf dem Stapel. 2) Es gibt keine Garantie dafür, dass der Fragmentmanager am Ende der Liste weiterhin neue hinzufügt. Sicher, dies geschieht in einfachen Tests, aber was ist, wenn ein Fragment entfernt wird und eine Null verbleibt und der Fragmentmanager dann unter bestimmten Umständen beschließt, diesen leeren Steckplatz wiederzuverwenden? Ich sage es nicht, aber es gibt keine Garantie dafür, dass dies unter keinen Umständen der Fall sein wird. 3) Wenn Sie oder ein zukünftiger Programmierer das Anhängen / Trennen zum Verwalten von Fragmenten verwenden, stimmt dies nicht mit dem Stapel überein.
ToolmakerSteve
Hallo, danke für deine Lösung, aber es ist nicht schön und einfach
Muhammad Ali
Ich bezweifle, dass es sich um eine funktionierende Lösung handelt. getFragments()Gibt eine verkürzte Sammlung von Fragmenten zurück. Vielleicht ist der letzte sichtbar, aber nicht viele andere.
CoolMind
5

Die Antwort von deepak goel funktioniert bei mir nicht, weil ich immer null bekomme entry.getName();

Ich setze ein Tag auf folgende Weise auf das Fragment:

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

Wobei ft meine Fragmenttransaktion und FRAGMENT_TAGdas Tag ist. Dann benutze ich diesen Code, um das Fragment zu erhalten:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
Eduardo
quelle
Dies funktioniert nur, wenn Sie allen Fragmenten das gleiche Tag geben. Dies ist keine gute Idee, wenn Sie später ein bestimmtes Fragment suchen möchten.
Shirane85
4

Habe gerade @roghayeh hosseini (richtige) Antwort genommen und es in Kotlin für die hier 2017 gemacht :)

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

* Dies sollte innerhalb einer Aktivität aufgerufen werden.

Genießen :)

Shirane85
quelle
3

Sie können getBackStackEntryAt () verwenden . Um zu wissen, wie viele Einträge die Aktivität im Backstack enthält, können Sie getBackStackEntryCount () verwenden.

int lastFragmentCount = getBackStackEntryCount() - 1;
Schwarzer Gürtel
quelle
11
Aber wie kann ich das letzte Fragment im Backstack bekommen? PopBackStackEntryAt () gibt nur eine BackStackEntry-Instanz zurück, NICHT fragment
Leem.fin
Ja, Sie haben Recht, aber jeder BackStackEntry enthält eine ID, die Sie mit getId () abrufen können. Sie können diese ID verwenden, um das Fragment abzurufen
Blackbelt
1
Um das letzte Fragment zu erhalten: getBackStackEntryCount () - 1
An-Droide
3
Diese Antwort ist falsch. Ich sehe, dass die Backstack-Einträge eine ID von 0 haben, daher kann das Fragment nicht anhand der ID abgerufen werden.
Justin
1
Das ist alt, aber für jeden, der hier wandert: Der Backstack enthält keine Fragmente, sondern Fragmenttransaktionen. Deshalb ist diese Antwort falsch
maciekjanusz
3

Ich werde Deepak Goels Antwort etwas hinzufügen, da viele Leute, ich eingeschlossen, mit seiner Methode eine Null bekommen haben. Anscheinend sollten Sie dies folgendermaßen tun, damit das Tag funktioniert, wenn Sie dem Backstack ein Fragment hinzufügen:

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

Sie müssen dasselbe Tag zweimal hinzufügen.

Ich hätte kommentiert, aber ich habe keine 50 Ruf.

Cosmin Vacaru
quelle
du hast jetzt 50 :)
davidbilla
2

Behalten Sie Ihren eigenen Backstack : myBackStack. Wenn Sie Addein Fragment zum haben FragmentManager, fügen Sie es auch hinzu myBackStack. In onBackStackChanged()Pop ab, myBackStackwenn seine Länge größer als ist getBackStackEntryCount.

Arne Evertsson
quelle
2

Es sieht so aus, als hätte sich etwas zum Besseren geändert, da der folgende Code perfekt für mich funktioniert, aber ich habe ihn in den bereits bereitgestellten Antworten nicht gefunden.

Kotlin:

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

Java:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)
Wassili Kravchenko
quelle
2

Wenn Sie verwenden addToBackStack(), können Sie folgenden Code verwenden.

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

Akbar Kazimov
quelle
1

Tatsächlich wird dem Stapel kein aktuelles Fragment hinzugefügt, da Sie mehrere oder Fragmente in einer einzigen Transaktion zum Stapel hinzufügen oder einfach Fragmente entfernen können, ohne ein neues hinzuzufügen.

Wenn Sie wirklich einen Fragmentstapel haben möchten und über seinen Index im Stapel auf ein Fragment zugreifen können möchten, sollten Sie eine Abstraktionsschicht über dem FragmentManagerund seinem Backstack haben. So geht's:

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

Anstatt nun das Hinzufügen und Fragmente durch den Aufruf zu entfernen FragmentManagerdirekt, sollten Sie verwenden push(), replace()und pop()Methoden FragmentStackManager. Und Sie können auf das oberste Fragment zugreifen, indem Sie einfach anrufenstack.get(stack.size() - 1) .

Aber wenn Sie Hacks mögen, habe ich andere Möglichkeiten, ähnliche Dinge zu tun. Das einzige, was ich erwähnen muss, ist, dass diese Hacks nur mit Support-Fragmenten funktionieren.

Der erste Hack besteht darin, alle aktiven Fragmente zum Fragmentmanager hinzuzufügen. Wenn Sie nur Fragmente einzeln ersetzen und die vom Stapel entfernen, gibt diese Methode das oberste Fragment zurück:

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

Der zweite Ansatz ist ereignisreicher und ermöglicht es Ihnen, alle Fragmente der letzten Transaktion hinzuzufügen, für die Folgendes addToBackStackaufgerufen wurde:

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

Bitte beachten Sie, dass Sie in diesem Fall diese Klasse in ein android.support.v4.appPaket packen müssen.

Michael
quelle
0

Oder Sie fügen einfach ein Tag hinzu, wenn Sie Fragmente hinzufügen, die ihrem Inhalt entsprechen, und verwenden ein einfaches statisches String-Feld (Sie können es auch im Aktivitätsinstanz-Bundle in der onSaveInstanceState (Bundle) -Methode speichern), um das zuletzt hinzugefügte Fragment-Tag zu speichern und dieses Fragment von Tag () abzurufen. jederzeit brauchen Sie ...

Евгений Шевченко
quelle
0

Die höchste Antwort (Deepak Goel) hat bei mir nicht gut funktioniert. Irgendwie wurde das Tag nicht richtig hinzugefügt.

Am Ende habe ich nur die ID des Fragments durch den Flow gesendet (mit Absichten) und sie direkt vom Fragmentmanager abgerufen.

htafoya
quelle