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

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


person Zsombor Erdődy-Nagy    schedule 23.05.2011    source источник


Ответы (7)


Похоже, в настоящее время в API отсутствует такой метод, как "getFragments".
Однако с помощью события "onAttachFragment" класса Activity сделать то, что вы хотите, довольно просто. Я бы сделал что-то вроде:

List<WeakReference<Fragment>> fragList = new ArrayList<WeakReference<Fragment>>();
@Override
public void onAttachFragment (Fragment fragment) {
    fragList.add(new WeakReference(fragment));
}

public List<Fragment> getActiveFragments() {
    ArrayList<Fragment> ret = new ArrayList<Fragment>();
    for(WeakReference<Fragment> ref : fragList) {
        Fragment f = ref.get();
        if(f != null) {
            if(f.isVisible()) {
                ret.add(f);
            }
        }
    }
    return ret;
}

В случае, если нет готового метода для чтения состояния объекта (isActive() в примере), я бы переопределил onResume и onPause, чтобы установить флаг (может быть просто логическое поле).
Это уже какая-то собственная бухгалтерия, но все же очень ограниченный, на мой взгляд, учитывая довольно конкретную цель, которую вы хотите достичь (список, зависящий от статуса).

person didi_X8    schedule 11.08.2011
comment
Хм, становится ли ссылка WeakReference‹Fragment› нулевой, если нет ссылок на фактический фрагмент, который был добавлен в список в onAttachFragment? - person Jon Willis; 12.08.2011
comment
Это прекрасно работает для моих целей. PS У меня есть строка: if (f != null && f.isVisible()) { ret.add(f); } - person Jon Willis; 12.08.2011
comment
Нет, ref сам по себе не станет нулевым, только get() вернет null, если активность была удалена сборщиком мусора. Таким образом, безопасно присваивать get() переменной f, а затем проверять ее на значение null. - person didi_X8; 13.08.2011
comment
Решение понравилось - person B. TIger; 28.06.2013
comment
В качестве аналогичного решения я передал события onAttach и onDetach Fragment в FragmentActivity через расширенный интерфейс обратного вызова (обычно используемый для отправки уведомлений о выборе элемента во фрагменте). Методы добавляют фрагменты во внутренний список, а затем удаляют их из него. Кажется, работает без необходимости использовать WeakReferences. - person Stan; 31.10.2013
comment
На самом деле библиотека поддержки предоставляет метод getFragments(), но его не следует использовать. Он помечен @hide и не должен был быть включен в jar библиотеки поддержки. Его не следует рассматривать как часть экспортируемого API. - person James Wald; 25.03.2014
comment
И как сделать, чтобы это работало для фрагментов, содержащих вложенные фрагменты? - person Snicolas; 19.06.2014
comment
Кстати, глядя на код Android, похоже, что это даст только фрагменты первого уровня, а не вложенные фрагменты. - person Snicolas; 20.06.2014
comment
Не должен ли обновляться список ссылок на фрагменты, если фрагмент отсоединен? В противном случае в этом списке могут быть фрагменты, которых уже давно нет (или, по крайней мере, их больше нет на экране). - person AgentKnopf; 10.11.2014
comment
Я использую Навигацию для изменения фрагментов, onAttachFragment вызывается только при загрузке первого фрагмента, никогда больше. - person sgm; 24.01.2021
comment
Обязательно очистите этот список. Иначе будут утечки памяти. Исходя из первого опыта. Агент Кнопф прав. Список должен обновиться. - person wseme; 02.02.2021

Если вы используете библиотеку поддержки Android, вы можете вызвать скрытый метод FragmentManager.getFragments():

public List<Fragment> getVisibleFragments() {
    List<Fragment> allFragments = getSupportFragmentManager().getFragments();
    if (allFragments == null || allFragments.isEmpty()) {
        return Collections.emptyList();
    }

    List<Fragment> visibleFragments = new ArrayList<Fragment>();
    for (Fragment fragment : allFragments) {
        if (fragment.isVisible()) {
            visibleFragments.add(fragment);
        }
    }
    return visibleFragments;
}
person Michael    schedule 04.12.2013
comment
Мы не должны использовать этот метод. Он помечен @hide и не должен был быть включен в jar библиотеки поддержки. Его не следует рассматривать как часть экспортируемого API. - person James Wald; 25.03.2014
comment
Если вы используете библиотеку поддержки и знаете, что внутри, вы можете вызывать любые методы, какие захотите. Худшее, что может случиться, это то, что после перехода на более новую версию библиотеки вы получите ошибки компиляции. - person Michael; 25.03.2014
comment
@Michael Одна важная вещь, которую я хотел бы отметить для этого метода для будущих зрителей (о чем, похоже, вы здесь рассказали), заключается в том, что он возвращает внутренний список фрагментов, а не его копию. Так что не изменяйте список, который вы возвращаете; создать экземпляр нового списка на основе возвращенного ответа и изменить этот один. В противном случае вы потратите целый день на отладку периодического исключения IndexOutOfBoundsException. :) - person Kevin Coppock; 03.05.2014
comment
Просто хочу отметить, как упоминал @Michael, с тех пор этот метод был удален. - person Jason Robinson; 17.08.2016
comment
Насколько я вижу, метод все еще виден, но есть ошибка - person Al Lelopath; 16.11.2016
comment
Поскольку android.googlesource.com/platform/frameworks/support/+/ метод getFragments ограничен библиотеками из одного и того же groupId, и Android Lint выдаст ошибку Lint, если вы его используете. - person Nahuel Barrios; 10.03.2017
comment
Да, это правда, но я уверен, что у команды Android есть веская причина ограничить использование этого метода, поэтому я думаю, что игнорировать эту ошибку — не очень хорошее решение. Кстати, RestrictedApi по умолчанию считается ошибкой с приоритетом 4/10 (tools.android. com/tips/lint-checks) - person Nahuel Barrios; 10.03.2017

Другой способ сделать это — пометить фрагменты тегами, когда вы добавляете их в действие.

Например, если вы динамически добавляете 4 фрагмента, вы можете добавить их так:

for (int i = 0; i < NUM_FRAGMENTS; i++){
    MyFragment fragment = new Fragment(i);
    fragmentTransaction.add(R.id.containerId, fragment, SOME_CUSTOM_PREFIX + i).commit()
}

Затем, если вам нужно вернуть все фрагменты позже, вы можете сделать:

for (int i = 0; i < NUM_FRAGMENTS; i++) {
     String tag = SOME_CUSTOM_PREFIX + i;
     fragmentList.add((MyFragment) fragmentManager.findFragmentByTag(tag));
}
person theelfismike    schedule 04.02.2013

Я решил с этим:

public ArrayList<Fragment> getAllFragments() {
    ArrayList<Fragment> lista = new ArrayList<Fragment>();

    for (Fragment fragment : getSupportFragmentManager().getFragments()) {
        try {
            fragment.getTag();
            lista.add(fragment);
        } catch (NullPointerException e) {

        }
    }

    return lista;

}
person Matheus Henrique da Silva    schedule 20.07.2014
comment
Я реализую что-то похожее на это, и я получаю NPE всякий раз, когда действие воссоздается и вызывается fragment.getTag(). Вы знаете, почему фрагмент равен нулю? - person Clocker; 14.11.2014
comment
Извините, но не. Работал просто для меня. - person Matheus Henrique da Silva; 11.12.2014

android.support.v4.app.FragmentManager есть метод getFragments(), который делает именно то, что вам нужно, но он никогда не был доступен. Внезапно, однако, это так, но я не уверен, предназначено ли это, потому что он все еще помечен как скрытый.

/**
 * Get a list of all fragments that have been added to the fragment manager.
 *
 * @return The list of all fragments or null if none.
 * @hide
 */
public abstract List<Fragment> getFragments();

Обычный android.app.FragmentManager вообще не имеет этого метода, но если действительно нужно, вы можете получить доступ к тому же объекту, получив поле mActive через отражение (будьте осторожны: начиная с API 26, его тип — SparseArray вместо List). Используйте на свой страх и риск :)

person 0101100101    schedule 30.08.2016
comment
Этот метод общедоступен, но ошибка - person Al Lelopath; 16.11.2016
comment
Нулевая проверка во время итерации исправляет это. - person 0101100101; 22.11.2016
comment
Он доступен для android.app.FragmentManager! - person Rahul Rastogi; 05.12.2016

Вот рекурсивное решение в Котлине. Учитывая самые верхние фрагменты действия, возвращает все дочерние фрагменты.

fun recursiveGetFragments(parents: List<Fragment>): List<Fragment> {
    val result = parents.toMutableList()
    for(root in parents) {
        if (root.isVisible) {
            result.addAll(recursiveGetFragments(root.childFragmentManager.fragments))
        }
    }
    return result
 }

Он используется как:

val fragmentList = recursiveGetFragments(supportFragmentManager.fragments)
person Kenan Soylu    schedule 24.05.2020

мы можем использовать какой-то неправильный, но законный метод

    ArrayList<Fragment> getActiveFragments() {
        Fragment f;
        final ArrayList<Fragment> fragments = new ArrayList<>();
        int i = 0;
        try {
            while ((f = getFragmentManager().getFragment(new Intent().putExtra("anyvalue", i++).getExtras(), "anyvalue")) != null) {
                fragments.add(f);
            }
        } catch (IllegalStateException ex) {

        }
        return fragments;
    }
person Yessy    schedule 21.03.2019