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

Как я могу получить последний экземпляр фрагмента, добавленный в 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 ??

person Leem.fin    schedule 14.03.2012    source источник


Ответы (17)


Вы можете использовать метод 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);
person Deepak Goel    schedule 20.03.2012
comment
это полезно. Независимо от того, какой фрагмент был недавно добавлен в стек, мы можем получить любой фрагмент в стеке по имени. - person Braj; 19.07.2013
comment
чтобы найти фрагмент по тегу, его необходимо добавить/заменить на тот же тег. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag) или FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) документ - person Saqib; 01.08.2013
comment
Проблема в том, что если у вас дважды есть фрагмент в заднем стеке, вы не можете получить конкретный фрагмент, когда все, что у вас есть, это индекс или сущность заднего стека. - person Justin; 29.05.2014
comment
Как говорит @Saqib, действительно ли получится извлечь фрагмент из имени тега обратной записи? Они не одинаковые или нет? - person Arne Evertsson; 15.08.2014
comment
Какой смысл называть это stack, если я не могу получить доступ к фрагменту с помощью метода pop? - person Kenny Worden; 20.03.2015
comment
comment
Кажется, если я использую имя из BackStackEntry для findFragmentByTag(), используя имя, возвращаемое записью, оно возвращает NULL, что похоже на ошибку - если я получаю список фрагментов из диспетчера фрагментов, фрагмент находится в списке, это на 4.4 .2 действительно раздражает. - person peterk; 19.09.2015
comment
конечно, если список представляет собой стек, то можно было бы использовать последнюю запись, но это не документировано таким образом. - person peterk; 19.09.2015
comment
@TeodorKolev Вы использовали один и тот же тег при добавлении или замене. FragmentTransaction.add(int containerViewId, фрагмент фрагмента, тег String) или FragmentTransaction.replace(int containerViewId, фрагмент фрагмента, тег String) - person Deepak Goel; 01.08.2016
comment
Да, чтобы вышеизложенное работало, вы должны использовать одно и то же строковое значение для тега фрагмента и имени обратного стека, иначе это не сработает. Так что это можно рассматривать как обходной путь, но не правильное решение. - person eC Droid; 08.08.2017
comment
Я не знаю почему, но findFragmentByTag возвращает null, даже когда отладчик ясно показывает, что тег в порядке. - person htafoya; 11.09.2017

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());
            }
        }
    });
person ashakirov    schedule 05.04.2014
comment
Этот ответ хорошо работает в моем проекте, поскольку я добавляю каждый фрагмент, кроме корневого, в задний стек. Но я предполагаю, что этот ответ не будет работать, если последний добавленный фрагмент не был добавлен в стопку. - person Arne Evertsson; 15.08.2014

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

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

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

Надеюсь это поможет!

person ptitvinou    schedule 20.01.2015

Котлин

// In activities
activity.supportFragmentManager.fragments.lastOrNull()

// In fragments
fragment.childFragmentManager.fragments.lastOrNull()
person The Finest Artist    schedule 28.05.2019
comment
Возможно, вам придется использовать .lastOrNull() в случае, если fragments пусто. - person Westy92; 18.09.2019

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

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}
person roghayeh hosseini    schedule 17.10.2017
comment
Спасибо. Самый элегантный ответ. Добавил сюда для любителей Kotlin stackoverflow.com/a/47336504/1761406 - person Shirane85; 16.11.2017

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

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;
}
person Erez    schedule 17.01.2016
comment
Ненадежный. Три причины: 1) Это список всех фрагментов, о которых знает диспетчер фрагментов, а не только тех, которые находятся в стеке. 2) Нет гарантии, что менеджер фрагментов будет продолжать добавлять новые фрагменты в конец списка. Конечно, в простых тестах так и будет, но что, если фрагмент удаляется, оставляя нуль, а затем при некоторых обстоятельствах диспетчер фрагментов решает повторно использовать этот пустой слот? Не говорю, что это так, но нет никакой гарантии, что это никогда не произойдет ни при каких обстоятельствах. 3) Если вы или какой-то будущий программист начнете использовать присоединение/отсоединение для управления фрагментами, это не будет соответствовать стеку. - person ToolmakerSteve; 20.09.2016
comment
Привет, спасибо за ваше решение, но оно не красивое и не простое - person Muhammad Ali; 17.10.2018
comment
Я сомневаюсь, что это рабочий раствор. getFragments() возвращает укороченный набор фрагментов. Может последний и виден, но не многие другие. - person CoolMind; 24.12.2019

Ответ, данный Deepak Goel, не работает для меня, потому что я всегда получаю null от entry.getName();

Что я делаю, так это устанавливаю тег для фрагмента следующим образом:

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

Где ft — моя фрагментная транзакция, а FRAGMENT_TAG — тег. Затем я использую этот код для получения фрагмента:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);
person Eduardo    schedule 30.10.2012
comment
Это будет работать только в том случае, если вы дадите всему фрагменту один и тот же тег, что не очень хорошая идея, если вы хотите найти конкретный фрагмент позже. - person Shirane85; 23.02.2017

Просто взял ответ @roghayeh hosseini (правильный) и сделал его в Котлине для тех, кто здесь в 2017 году :)

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

* Это следует вызывать из действия.

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

person Shirane85    schedule 16.11.2017

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

int lastFragmentCount = getBackStackEntryCount() - 1;
person Blackbelt    schedule 14.03.2012
comment
Но как я могу получить последний фрагмент в бэкстеке?? PopBackStackEntryAt() возвращает только экземпляр BackStackEntry, а НЕ фрагмент - person Leem.fin; 14.03.2012
comment
да, вы правы, но каждый BackStackEntry содержит идентификатор, который вы можете получить с помощью getId(). вы можете использовать этот идентификатор, чтобы получить фрагмент - person Blackbelt; 14.03.2012
comment
Чтобы получить последний фрагмент: getBackStackEntryCount() - 1 - person An-droid; 08.04.2013
comment
Но затем, чтобы получить этот фрагмент по идентификатору (getId()), вам нужно поддерживать свои собственные стеки, независимые от обратного стека Android). - person ; 05.03.2014
comment
Этот ответ неверен, я вижу, что записи обратного стека имеют идентификатор 0, поэтому не могу получить фрагмент по идентификатору. - person Justin; 29.05.2014
comment
@Justin в документации сказано: Вернуть уникальный идентификатор записи. Это единственное представление записи, которое будет сохраняться в экземплярах действия. - person Blackbelt; 29.05.2014
comment
Это старо, но для всех, кто здесь бродит: задний стек содержит не фрагменты, а транзакции фрагментов, поэтому этот ответ неверен - person maciekjanusz; 31.05.2016
comment
1) Я думаю, вы имеете в виду getBackStackEntryAt, а не popBackStackEntryAt? 2) Несмотря на негативные комментарии, getId() полезен в большинстве случаев, когда есть только один фрагмент каждого типа. Затем используйте fragmentManager.findFragmentById( id ). Ограничение состоит в том, что если тип фрагмента увеличивается несколько раз, можно найти только один из них. В этом случае используйте tags (имена): stackoverflow.com/a/28046389/199364 - person ToolmakerSteve; 20.09.2016
comment
@ToolmakerSteve, ответу 4 года - person Blackbelt; 20.09.2016

Похоже, что-то изменилось к лучшему, потому что код ниже у меня отлично работает, но я не нашел его в уже предоставленных ответах.

Котлин:

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

Ява:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)
person Vasily Kravchenko    schedule 10.11.2018

Я добавлю кое-что к Deepak Goel answer, так как многие люди, включая меня, получали нуль, используя его метод. По-видимому, чтобы заставить тег работать, когда вы добавляете фрагмент в стопку, вы должны делать это следующим образом:

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

Вам нужно добавить один и тот же тег дважды.

Я бы прокомментировал, но у меня нет репутации 50.

person Cosmin Vacaru    schedule 11.03.2020
comment
тебе уже 50 :) - person davidbilla; 11.03.2020

Держите свой собственный задний стек: myBackStack. Поскольку вы Add добавляете фрагмент к FragmentManager, добавьте его и к myBackStack. В onBackStackChanged() извлекается из myBackStack, когда его длина превышает getBackStackEntryCount.

person Arne Evertsson    schedule 15.08.2014

Если вы используете addToBackStack(), вы можете использовать следующий код.

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

person Akbar Kazimov    schedule 01.08.2019

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

Если вы действительно хотите иметь стек фрагментов и иметь доступ к фрагменту по его индексу в стеке, вам лучше иметь слой абстракции над FragmentManager и его задним стеком. Вот как это сделать:

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.

person Michael    schedule 11.02.2016

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

person Евгений Шевченк&    schedule 03.11.2014

Самый высокий (Deepak Goel) ответ не сработал для меня. Почему-то тег не был добавлен должным образом.

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

person htafoya    schedule 13.09.2017

Разработчики Kotlin могут использовать это для получения текущего фрагмента:

        supportFragmentManager.addOnBackStackChangedListener {
        val myFragment = supportFragmentManager.fragments.last()

        if (null != myFragment && myFragment is HomeFragment) {
            //HomeFragment is visible or currently loaded
        } else {
            //your code
        }
    }
person Kishan Solanki    schedule 01.09.2020