получить последний фрагмент в стеке

105

Как я могу добавить последний экземпляр фрагмента в backstack (если я не знаю тег и идентификатор фрагмента)?

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
источник

Ответы:

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

Вам нужно убедиться, что вы добавили фрагмент в backstack следующим образом:

fragmentTransaction.addToBackStack(tag);
Дипак Гоэль
источник
24
чтобы найти фрагмент по тегу, его нужно добавить / заменить на такой же тег. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)или FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib 01
3
Проблема с этим в том, что если у вас есть фрагмент в заднем стеке дважды, вы не можете получить конкретный фрагмент, когда все, что у вас есть, - это объект index или backstackentry.
Джастин
14
Какой смысл называть его a, stackесли я не могу получить доступ к фрагменту с помощью метода pop?
Кенни Уорден,
1
Я не знаю почему, но findFragmentByTag возвращает значение null, даже когда отладчик ясно показывает, что с тегом все в порядке.
htafoya
49
FragmentManager.findFragmentById(fragmentsContainerId) 

функция возвращает ссылку на вершину Fragmentв стеке. Пример использования:

    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
Этот ответ хорошо работает в моем проекте, так как я добавляю каждый фрагмент, кроме корневого, в задний стек. Но я предполагаю, что этот ответ не сработает, если последний добавленный фрагмент не был добавлен в backstack.
Арне Эвертссон
41

Я лично пробовал многие из этих решений и в итоге получил следующее рабочее решение:

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

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

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

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

Наконец, вы можете найти любой из ваших фрагментов в своем backstack с помощью этого метода:

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

Следовательно, получить верхний фрагмент в backstack можно легко, вызвав:

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
Спасибо. Самый элегантный ответ. Добавил сюда для любителей Kotlin stackoverflow.com/a/47336504/1761406
Shirane85
7

Котлин

activity.supportFragmentManager.fragments.last()
Лучший художник
источник
4
Возможно, вам понадобится использовать, .lastOrNull()если fragmentsон пуст.
Westy92
6

В fragmentMananger есть список фрагментов. Имейте в виду, что удаление фрагмента не приводит к уменьшению размера списка (запись фрагмента просто становится нулевой). Следовательно, правильным решением будет:

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

Ответ Deepak Goel не работает для меня, потому что я всегда получаю null от 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)
        }
    }
}

* Это должно вызываться из Activity.

Наслаждаться :)

Ширане85
источник
3

вы можете использовать getBackStackEntryAt () . Чтобы узнать, сколько записей содержит активность в backstack, вы можете использовать getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;
Черный пояс
источник
11
Но как мне получить последний фрагмент в стопке ?? PopBackStackEntryAt () возвращает только экземпляр BackStackEntry, а НЕ фрагмент
Leem.fin
да, вы правы, но каждый BackStackEntry содержит идентификатор, который вы можете получить с помощью getId (). вы можете использовать этот идентификатор, чтобы получить фрагмент
Blackbelt
1
Чтобы получить последний фрагмент: getBackStackEntryCount () - 1
An-droid
3
Этот ответ неверен, я вижу, что записи backstack имеют идентификатор 0, поэтому не могу получить фрагмент по идентификатору.
Джастин
1
Это старое, но для всех, кто здесь заблудился: задний стек не содержит фрагментов, а фрагментных транзакций, поэтому этот ответ неверен
maciekjanusz
3

Я добавлю кое-что к ответу Дипака Гоэла, поскольку многие люди, в том числе я, получали нуль, используя его метод. Видимо, чтобы тег работал, когда вы добавляете фрагмент в backstack, вы должны делать это так:

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]

Ява:

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.apppackage.

Майкл
источник
0

Или вы можете просто добавить тег при добавлении фрагментов, соответствующих их содержимому, и использовать простое статическое поле String (также вы можете сохранить его в пакете экземпляров активности в методе onSaveInstanceState (Bundle)), чтобы сохранить последний добавленный тег фрагмента и получить этот фрагмент с помощьюTag () в любое время вам нужно ...

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

Самый высокий ответ (Дипак Гоэль) мне не подошел. Как-то тег не был добавлен должным образом.

В итоге я просто отправил идентификатор фрагмента через поток (используя намерения) и получил его непосредственно из диспетчера фрагментов.

Хтафоя
источник