Фрагменти всередині фрагментів


145

Мені цікаво, чи це насправді помилка в API Android:

У мене таке налаштування:

┌----┬---------┐
|    |         |
|  1 |    2    |
|    |┌-------┐|
|    ||       ||
|    ||   3   ||
└----┴┴-------┴┘
  1. Це меню, яке завантажує фрагмент №2 (Екран пошуку) на правій панелі.
  2. Це екран пошуку, який містить фрагмент №3, який є списком результатів.
  3. Список результатів використовується в декількох місцях (в тому числі як функціонуючий фрагмент високого рівня в самому собі).

Ця функціональність чудово працює в телефоні (де 1 і 2 і 3 - це ActivityFragments).

Однак, коли я використовував цей код:

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();       
    Fragment frag = new FragmentNumber2();
    if(toLoad != null) frag.setArguments(toLoad);
    transaction.replace(R.id.rightPane, frag);      
    transaction.commit();

Де R.id.leftPaneі R.id.rightPaneзнаходяться <fragment>в горизонтальній лінійній схемі.

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

07-27 15:22:55.940: ERROR/AndroidRuntime(8105): Caused by: java.lang.IllegalArgumentException: Binary XML file line #57: Duplicate id 0x7f080024, tag null, or parent id 0x0 with another fragment for FragmentNumber3

Це викликано тим, що контейнер для FragmentNumber3 був дублюється, і він більше не має унікального ідентифікатора. Початковий фрагмент не був знищений (?) До того, як буде доданий новий (на мій погляд, це означає, що він не був замінений ).

Може хтось скаже мені, чи це можливо ( ця відповідь говорить про те, що це не так) чи це помилка?


1
можливий дублікат Fragment Inside Fragment
rds

6
@rds - це давнє запитання, трохи безглуздо маркувати як дублікат.
pietv8x

Відповіді:


203

Вкладені фрагменти наразі не підтримуються. Спроба помістити фрагмент в інтерфейс іншого фрагмента призведе до невизначеної та ймовірної порушеної поведінки.

Оновлення : Вкладені фрагменти підтримуються на версії Android 4.2 (і бібліотеки підтримки Android, версія 11): http://developer.android.com/about/versions/android-4.2.html#NestedFragments

ПРИМІТКА (згідно з цим документом ): " Примітка. Ви не можете надути макет у фрагмент, якщо цей макет включає <fragment>. Вкладені фрагменти підтримуються лише тоді, коли динамічно додаються до фрагменту. "


14
Не підтримується, оскільки це не було ціллю дизайну для початкової реалізації. Я чув багато запитів про цю функцію, тому, ймовірно, це буде зроблено в якийсь момент, але, як завжди, багато інших речей конкурують з нею в пріоритеті.
hackbod

4
Мені це вдалося, розширивши FragmentActivity, FragmentManager і FragmentTransaction. Основна передумова - це розширення DeferringFragmentActivity в моїй діяльності, що забезпечує те саме api, щоб інші зміни коду не змінювалися. Коли я викликаю getFragmentManager, я отримую екземпляр, що DeferringFragmentManager, і коли я викликаю beginTransaction, я отримую DeferredTransaction. Ця транзакція зберігає POJO з викликаним методом та аргументами. Коли фіксується дзвінок, ми спочатку шукаємо будь-які очікувані DeferredTransaction. Після того, як всі транзакції будуть здійснені, ми починаємо реальну транзакцію та запускаємо всі збережені методи з арґугами.
dskinner

11
Цей момент зараз. Вкладені Fragments тепер є частиною Android API, так! developer.android.com/about/versions/… .
Алекс Локвуд

9
Нічого собі, який кошмар: якщо ви використовуєте <fragment> на фрагменті, і цей фрагмент трапляється використовувати дочірні фрагменти, він не виходить з явної помилки ("не можна додати дочірні фрагменти до фрагментів макета") - це провалюється загадково за винятками на кшталт "фрагмент не створив вигляд". Існує кілька годин часу налагодження ...
Глен Мейнард

6
@ MartínMarconcini впевнений, але це зовсім не очевидно, виходячи з функціональності, що надається API. Якщо щось не дозволено, це повинно бути чітко задокументовано, а не залишати розробнику витягати волосся, бо щось не працює так, як ви очікували.
привіт

98

Вкладені фрагменти підтримуються в Android 4.2 та пізніших версіях

Тепер Бібліотека підтримки Android також підтримує вкладені фрагменти , тому ви можете реалізувати вкладені дизайни фрагментів на Android 1.6 і новіших версіях.

Щоб вкласти фрагмент, просто зателефонуйте getChildFragmentManager () на фрагмент, у який потрібно додати фрагмент. Це повертає FragmentManager, який ви можете використовувати, як зазвичай, для здійснення операцій верхнього рівня для створення транзакцій з фрагментами. Наприклад, ось код, який додає фрагмент із наявного класу Fragment:

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

Щоб отримати більше уявлення про вкладені фрагменти, перегляньте ці підручники
Частина 1
Частина 2
Частина 3

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


Основним недоліком Nestedfragment є те, що ми не можемо викликати параметр меню з дочірнього фрагмента :( якщо ми використовуємо ABS!
LOG_TAG

Ви можете, будь ласка, заглянути в моєму номері ?? Його дуже схоже .. stackoverflow.com/questions/32240138/… . Для мене дитячий framnet не завищений від коду
Nicks

33

.. ви можете очистити вкладений фрагмент destroyviewметодом батьківського фрагмента :

@Override
    public void onDestroyView() {

      try{
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.remove(nestedFragment);

        transaction.commit();
      }catch(Exception e){
      }

        super.onDestroyView();
    }

4
Якщо ви зробите тестування життєвого циклу з SetAlwaysFinish ( bricolsoftconsulting.com/2011/12/23/… ), ви побачите, що цей код спричиняє помилку, коли інша активність перебуває на вершині з увімкненим завжди завершенням (IllegalStateException: Не можна виконати цю дію після onSaveInstanceState). Загортання коду вище у програмі try / catch - не найелегантніше рішення, але, здається, все працює.
Тео

Це майже спрацювало. Пізніше я отримав Stackoverflow на графічному інтерфейсі. Однозначно уникайте вкладених фрагментів ...
neteinstein

14

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

Я отримував таку ж помилку, коли намагався запустити додаток. Схоже, якщо ви будете інстанціювати фрагменти в макеті xml після того, як вкладку не було вибрано, а потім переобрано, я отримав би помилку надувача.

Я вирішив це, замінивши всі фрагменти в xml на Linearlayouts, а потім застосував менеджер фрагментів / операцію з фрагментами для інстанціювання фрагментів, і все, здається, працює коректно, принаймні на рівні тесту.

Я сподіваюся, що це вам допоможе.


Чи може хтось прокоментувати ефективність такого підходу? Мені прикро, що можна використовувати Фрагменти лише на глибині одного рівня - можливо, і взагалі не використовувати їх. Додавання їх програмно до груп представників заповнення буде працювати без застережень?
Рафаель Нобре

Все ще здається, що це працює на мене, я поміняю їх і не переглядаючи глядачів. Одне застереження: я роблю це лише на сотах, не сумісність із сендвіч-морозивом.
draksia

4

Я зіткнувся з тією ж проблемою, боровся з нею пару днів, і слід сказати, що найпростіший спосіб подолання, який я виявив, - це використовувати fragment.hide () / fragment.show (), коли вкладка вибрана / невибрана ().

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
    if (mFragment != null)
        ft.hide(mFragment);
}

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

Цей підхід має ще одну додаткову перевагу - використання hid () / show () не призводить до того, що представлення фрагментів втрачає їх стан, тому немає необхідності відновити попереднє положення прокрутки для ScrollViews, наприклад.

Проблема полягає в тому, що я не знаю, чи правильно не відривати фрагменти, коли вони не видно. Я думаю, що офіційний приклад TabListener розроблений з урахуванням того, що фрагменти багаторазові, і ви не повинні забруднювати їх пам'яттю, однак, я думаю, якщо у вас є лише кілька вкладок і ви знаєте, що користувачі будуть часто перемикатися між ними буде доцільно тримати їх прив’язаними до поточної діяльності.

Я хотів би почути коментарі більш досвідчених розробників.


0

Якщо ви виявите, що вкладений фрагмент не видаляється чи не дублюється (наприклад, при перезапуску активності, на екрані обертання), спробуйте змінити:

transaction.add(R.id.placeholder, newFragment);

до

transaction.replace(R.id.placeholder, newFragment);

Якщо вище не допомагає, спробуйте:

Fragment f = getChildFragmentManager().findFragmentById(R.id.placeholder);

FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

if (f == null) {
    Log.d(TAG, "onCreateView: fragment doesn't exist");
    newFragment= new MyFragmentType();
    transaction.add(R.id.placeholder, newFragment);
} else {
    Log.d(TAG, "onCreateView: fragment already exists");
    transaction.replace(R.id.placeholder, f);
}
transaction.commit();

Дізналися тут

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