отримати найновіший фрагмент у backstack


104

Як я можу додати останній екземпляр фрагмента в backstack (якщо я не знаю тег & id фрагмента)?

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 ??

Відповіді:


156

Ви можете використовувати getName()метод, FragmentManager.BackStackEntryякий було введено в API рівня 14. Цей метод поверне тег, який був тим, який ви використовували, коли ви додали фрагмент до рюкзака addTobackStack(tag).

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

Вам потрібно переконатися, що ви додали фрагмент до рюкзака так:

fragmentTransaction.addToBackStack(tag);

24
щоб знайти фрагмент за тегом, його потрібно додати / замінити тим самим тегом. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)або FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib

3
Проблема з цим полягає в тому, що якщо у вас є фрагмент у задній стеці двічі, ви не можете отримати конкретний фрагмент, коли все, що у вас є, є об'єктом індексу або заднім числом.
Джастін

14
Який сенс називати це a, stackякщо я не можу отримати доступ до фрагмента за допомогою pop-методу?
Kenny Worden


1
Я не знаю, чому, але findFragmentByTag повертається до нуля, навіть коли налагоджувач чітко показує, що тег у порядку.
хтафоя

49
FragmentManager.findFragmentById(fragmentsContainerId) 

функція повертає посилання на верхню Fragmentчастину в backstack. Приклад використання:

    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
Ця відповідь добре працює в моєму проекті, оскільки я додаю кожен фрагмент, крім кореневого фрагмента, у задній стек. Але я думаю, що ця відповідь не спрацює, якщо останній доданий фрагмент не був доданий до рюкзака.
Арне Евертссон

40

Я особисто спробував багато таких рішень і закінчив це робоче рішення:

Додайте цей утилітний метод, який буде використано кілька разів нижче, щоб отримати кількість фрагментів у вашій запчастині:

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

Потім, додаючи / замінюючи фрагмент, використовуючи метод FragmentTransaction, генеруйте унікальний тег для вашого фрагмента (наприклад: за допомогою кількості фрагментів у вашому стеку):

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

Нарешті, за допомогою цього методу ви можете знайти будь-який з ваших фрагментів у задній кулі:

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

Тому витяг верхнього фрагмента у вашій рюкзаці можна легко досягти, зателефонувавши:

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

Сподіваюся, це допомагає!


10

цей помічний метод отримати фрагмент зверху стека:

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

1
Дякую. Найелегантніша відповідь. Додано його тут для любителів Котлин stackoverflow.com/a/47336504/1761406
Shirane85


6

Існує список фрагментів у фрагменті «Манангер». Майте на увазі, що видалення фрагмента не призводить до зменшення розміру списку (запис фрагмента просто перетворюється на нуль). Тому правильним рішенням було б:

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
Не надійний. Три причини: 1) Це список усіх фрагментів, про які знає менеджер фрагментів, а не лише тих, що знаходяться в стеку. 2) Немає гарантії, що менеджер фрагментів продовжить додавати нові в кінці списку. Звичайно, це буде зроблено в простих тестах, але що робити, якщо фрагмент буде видалений, залишивши нуль, а потім за деяких обставин менеджер фрагментів вирішить повторно використовувати цей порожній слот? Не кажучи, що це робить, але немає жодної гарантії, що це ніколи, ні за яких обставин. 3) Якщо ви або якийсь майбутній програміст починає використовувати приєднання / відключення для управління фрагментами, це не буде відповідати стеку.
ToolmakerSteve

Привіт, дякую за ваше рішення, але це не красиво і просто
Мухаммед Алі

Я сумніваюся, що це робочий солютуїн. getFragments()повертає скорочену колекцію фрагментів. Можливо, видно останнє, але не багато інших.
CoolMind

5

Відповідь, яку надає деепак голль, не працює для мене, тому що я завжди отримую нуль entry.getName();

Що я роблю, це встановити тег до фрагменту таким чином:

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

Де ft - моя транзакція з фрагментами і FRAGMENT_TAGє тегом. Потім я використовую цей код, щоб отримати фрагмент:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);

Це спрацює лише в тому випадку, якщо ви надасте всім фрагментам один і той же тег, що не є хорошою ідеєю, якщо ви хочете знайти конкретний фрагмент пізніше.
Shirane85

4

Щойно я взяв @roghayeh hosseini (правильну) відповідь і зробив це в Котліні для тих, хто тут у 2017 році :)

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

* Це потрібно викликати зсередини діяльності.

Насолоджуйтесь :)


3

ви можете використовувати getBackStackEntryAt () . Для того, щоб знати, скільки записів має активність у backstack, ви можете використовувати getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;

11
Але як я можу отримати останній фрагмент у backstack ?? ПопBackStackEntryAt () повертає лише екземпляр BackStackEntry, а не фрагмент
Leem.fin

так, ви праві, але кожен BackStackEntry містить і ідентифікатор, який ви можете отримати за допомогою getId (). Ви можете використовувати цей Id для пошуку фрагмента
Blackbelt

1
Щоб отримати останній фрагмент: getBackStackEntryCount () - 1
Ан-дроїд

3
Ця відповідь неправильна, я бачу, що записи в backstack мають ідентифікатор 0, тому не можу отримати фрагмент за id.
Джастін

1
Це старе, але для всіх, хто тут блукає: задній стек містить не фрагменти, а фрагменти трансакцій, тому ця відповідь помилкова
maciekjanusz

3

Я додам щось у відповідь Діпака Гоеля, оскільки багато людей, включаючи мене, отримували нуль, використовуючи його метод. Мабуть, щоб тег працював, коли ви додавали фрагмент до рюкзака, ви повинні робити це так:

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

Потрібно додати один і той же тег двічі.

Я б прокоментував, але у мене немає 50 репутації.


вам зараз 50:)
davidbilla

2

Тримайте свій власний стек назад: myBackStack. Як Addфрагмент до FragmentManager, також додайте його myBackStack. У onBackStackChanged()попсі, myBackStackколи його довжина більша за getBackStackEntryCount.


2

Схоже, щось змінилося в кращу сторону, оскільки код нижче для мене прекрасно працює, але я його не знайшов у вже наданих відповідях.

Котлін:

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

Java:

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

2

Якщо ви використовуєте addToBackStack(), ви можете використовувати наступний код.

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


1

Насправді до стеку не доданий останній фрагмент, тому що ви можете додати кілька або фрагментів до стеку за одну транзакцію або просто видалити фрагменти без додавання нового.

Якщо ви дійсно хочете мати стопку фрагментів і мати можливість отримати доступ до фрагменту за його індексом у стеці, вам краще мати шар абстракції над FragmentManagerта його backstack. Ось як це можна зробити:

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

Тепер замість додавання і видалення фрагментів шляхом виклику FragmentManagerбезпосередньо, ви повинні використовувати push(), replace()і pop()методи FragmentStackManager. І ви зможете отримати доступ до найвищого фрагмента, просто зателефонувавши stack.get(stack.size() - 1).

Але якщо вам подобаються хаки, я маю інші способи робити подібні речі. Єдине, що я мушу зазначити, це те, що ці хаки працюватимуть лише з фрагментами підтримки.

Перший хак - це просто отримати всі активні фрагменти, додані до менеджера фрагментів. Якщо ви просто заміните фрагменти один за одним і виведете зі стека, цей метод поверне найвищий фрагмент:

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

Другий підхід є подій більш хакітним і дозволяє отримати всі фрагменти, додані в останній транзакції, для якої addToBackStackбуло викликано:

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

Будь ласка, зауважте, що в цьому випадку ви повинні класти цей клас у android.support.v4.appпакет.


0

Або ви можете просто додати тег, додаючи фрагменти, що відповідають їх вмісту, та використовувати просте статичне поле String (також ви можете зберегти його в пакеті екземпляра діяльності методом onSaveInstanceState (Bundle), щоб утримати останній доданий фрагмент тегу і отримати цей фрагмент byTag () у будь-який час, коли вам потрібно ...


0

Найвища відповідь (Діпака Гоеля) не спрацювала для мене. Якось тег не було додано належним чином.

Я в кінцевому підсумку просто надсилав ідентифікатор фрагмента через потік (використовуючи наміри) та отримував його безпосередньо з менеджера фрагментів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.