IllegalStateException: Фрагмент, вже доданий у фрагмент вкладки


81
FATAL EXCEPTION: main
Process: com.example.loan, PID: 24169
java.lang.IllegalStateException: Fragment already added: FormFragment{428f10c8 #1 id=0x7f050055 form}
    at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1192)
    at android.support.v4.app.BackStackRecord.popFromBackStack(BackStackRecord.java:722)
    at android.support.v4.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1533)
    at android.support.v4.app.FragmentManagerImpl$2.run(FragmentManager.java:489)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1484)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:450)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5068)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
    at dalvik.system.NativeStart.main(Native Method)

Отже, у мене є андроїд-додаток, який створюється за допомогою tabhost. Всього є три вкладки, на вкладці2 є кнопка для здійснення транзакції фрагмента на вкладці2 (яка викликає функцію в дії фрагмента)

FragmentTransaction t = getSupportFragmentManager().beginTransaction();
        t.replace(R.id.realtabcontent, mFrag);
        t.addToBackStack(null);
        t.commit();

Є виняток, якщо я запускаю так:

  1. Усередині вкладки2 я натискаю кнопку, щоб змінити фрагмент
  2. Перейдіть на іншу вкладку (наприклад, вкладку 1 або вкладку 3)
  3. Натисніть кнопку назад
  4. Киньте виняток

Як це виправити? Дякуємо за допомогу



це означає, що backpress додає новий фрагмент, яка логіка в backstack? Велике спасибі
— користувач782104

mFrag додано на якусь іншу вкладку, крім tab2?
— Akhil

Відповіді:


152

Це трапляється, коли ми намагаємось додати один і той же фрагмент або DialogFragment двічі перед тим, як відхилити,

просто зателефонуйте

if(mFragment.isAdded())
{
     return; //or return false/true, based on where you are calling from
}

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


5
Це має бути прийнятою відповіддю. Прийнята відповідь не має сенсу. По-перше, не повинно бути заперечення isAdded(). По-друге, у коментарях пропонується ввести цей код onCreate(), що також є безглуздим. Цей рядок коду слід розміщувати безпосередньо перед рядком, куди додається (або замінюється) фрагмент, а не в onCreate()або onCreateView(). Запізно виконувати цей код за будь-яким із цих методів.
— AndroidDev

3
if(fragment.isAdded()) fragmentTransaction.show(fragment);
— Костянтин Конопко

також потрібно перевірити на предмет приховування фрагментів.
— Махді Мокадасі

Мені потрібно додати подібні фрагменти, і лише атрибути у фрагменті відрізняються. Але дубльована помилка все одно трапляється.
— Алстон,

я вже реалізував, щоб перевірити, чи фрагмент вже додано, а потім повернено (код додавання фрагмента не буде виконаний), оскільки моє додаток працює в магазині відтворення, я все-таки отримав фрагмент аварійного завершення з деяких пристроїв, які не всі пристрої, які я намагаюся зрозуміти з місяця тому, я не можу це зробити, я застосував кілька рішень і оновив збірку в магазині живих ігор, все ще я отримую помилку від деяких пристроїв як Galaxy S20 + 5G, huawei, там менше пристроїв, які мають проблему, яку я отримав, будь-яка допомога буде вдячна спасибі
— Navin

12

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

FragmentManager fm = getSupportFragmentManager();
Fragment oldFragment = fm.findFragmentByTag("fragment_tag");
if (oldFragment != null) {
    fm.beginTransaction().remove(oldFragment).commit();
}
MyFragment newFragment = new MyFragment();
fm.beginTransaction().add(newFragment , "fragment_tag");

чи не слід надсилати аргументи для внесення змін до вже відображеного фрагмента? замість того, щоб видалити та додати його знову
— Ujju,

Ви не можете встановити аргументи після того, як фрагмент був доданий до менеджера фрагментів. З документів: "Цей метод не можна викликати, якщо фрагмент додано до FragmentManager і якщо isStateSaved () поверне true." Тому вам потрібно зателефонувати до методів фрагментів безпосередньо, якщо ви хочете оновити інтерфейс.
— vovahost

так, я не мав на увазі налаштування через setArguments, мав на увазі надсилання аргументів як параметрів для оновлення, тож видалення та додавання того самого фрагмента не має ніякої користі, чи не так?
— Ujju

Це залежить. Якщо вам потрібно переналаштувати багато речей, можливо, буде простіше просто замінити фрагмент на новий. Все залежить від ваших потреб.
— vovahost

1
Я продовжую отримувати помилку, якщо використовую одну транзакцію. Можливо, це пов'язано з асинхронною поведінкою роботи з менеджером фрагментів.
— CoolMind

8

Вам просто потрібно перевірити одну умову у вашому фрагменті, згаданому нижче:

if(!isAdded())
{
    return;
}

isAdded = Повернути true, якщо фрагмент наразі додано до своєї діяльності. Взято з офіційного документа. Це не додасть цей фрагмент, якщо він уже доданий

Перевірте посилання нижче для посилання:
http://developer.android.com/reference/android/app/Fragment.html#isAdded ()


дякую за допомогу, ви маєте на увазі, що я помістив if (! isAdded ()) всередину oncreateview?
— user782104

Так, вам просто потрібно помістити той код, про який я згадав у своїй відповіді вище ... Це означає, що ваш фрагмент вже додано в стек. Отже, не потрібно додавати його знову, і він повертається просто.
— Deep Mehta

6
це не має сенсу, ви не можете повернути порожнечу в onCreateView, ви мали на увазі onCreate? я спробував це там, і це не допомогло моїй проблемі
— Fonix

5
Це додано до onCreate?
— StuStirling

5
Чому знак заперечення? Чи не слід додавати фрагмент, якщо! IsAdded ()?
— Alen Siljak

7

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

Отже, перевірте макет фрагмента.


2
Дякую, для мене було те саме. Повідомлення про виняток не може бути більш оманливим.
— SqueezyMo

Після більш ніж одного місяця розслідування першопричини, це було причиною. Велике спасибі
— Омід Гешматінія

7

Вам потрібно лише перевірити одну умову перед початком транзакції фрагмента

 if (!fragmentOne.isAdded()){
            transaction = manager.beginTransaction();
            transaction.add(R.id.group,fragmentOne,"Fragment_One");
            transaction.commit();
 }

це ідеально працює для мене ...


1

У мене є ця помилка, коли я не обертаю своє тіло XML усередині ViewGroup всередині FrameLayout.

Помилка:

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".screens.home.HomeEpoxyFragment">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv"
            android:overScrollMode="never"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</FrameLayout>

Вирішено:

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".screens.home.HomeEpoxyFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv"
                android:overScrollMode="never"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

Сподіваюся, це може комусь допомогти.


0

Для мене це працює так:

Fragment oldFragment = manager.findFragmentByTag(READER_VIEW_POPUP);
if (oldFragment != null) {
    manager.beginTransaction().remove(oldFragment).commit();
}

FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();

0

Це навіть може статися, якщо FragmentStatePagerAdapterз вашого ViewPagerви створите елемент, який уже існує:

override fun getItem(position: Int): Fragment {
    return tabs[0] // Right variant: tabs[position]
}

( private val tabs: List<Fragment>це список фрагментів на вкладках).


Як ти це вирішив? Це дуже схоже на мій варіант використання
— Мітч,

@Mitch, я писав return tabs[position]у другому рядку.
— CoolMind

Я також використовую ту ж концепцію, return tabs[position]що і раніше, але все одно видає помилку
— Мітч,

@Mitch, вибачте, я не знаю без коду. Для загального випадку в даний час я використовую stackoverflow.com/a/57161814/2914140 . Як ви думаєте, чи FragmentStatePagerAdapterвикидає помилку getItem?
— CoolMind

Дуже ймовірно, що це джерело помилки, оскільки саме ViewPagerтут витягуються екземпляри (фрагменти) фрагментів, показані
— Мітч,

0

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

if (!FirebaseManager.isClientA && !FirebaseManager.isClientB) {
      fragment = new FragmentA();
      getFragmentManager().beginTransaction().add(R.id.fragment_frame, fragment, null).addToBackStack("").commit();
} else if (FirebaseManager.isClientB) {
      fragment = new FragmentB();
} else {
      fragment = new FragmentC();
}
getFragmentManager().beginTransaction().add(R.id.fragment_frame, fragment, null).addToBackStack("").commit();

Переконайтесь, що не допустили тієї ж помилки.


0

Додайте фрагмент, як показано нижче

FragmentTransaction t = getSupportFragmentManager().beginTransaction();
    t.replace(R.id.realtabcontent, mFrag);
    t.addToBackStack(null);
    t.commitNowAllowingStateLoss();

Що буде зі старими та новими фрагментами? Чи замінить старе новим?
— CoolMind

Ні, це не замінить, він додасть попередній фрагмент у задній стек.
— М.Номан

Цікаво, чи можливо це? Вчора я спробував подібну код і отримав повідомлення про помилку, див stackoverflow.com/questions/38566628 / ... , тому що addToBackStackне допускаються разом з commitNow.
— CoolMind

У цьому посиланні він вручну відключає зворотний стек. За допомогою addToBackStack це дозволить управляти зворотним стеком фрагмента
— M.Noman

0

Ви можете спробувати це:

 if (dialogFolderGallery.isAdded()) {
                dialogFolderGallery.dismiss();
            } else { //bla...bla..
}

0

Я отримав цю помилку при неправильному використанні мого ViewModel всередині Фрагменту, як це:

//This is wrong!
MyViewModel viewModel = new MyViewModel(getActivity().getApplication());

Правильний спосіб:

viewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(MyViewModel.class);

0

Ось моє рішення для діалогового фрагмента (ви можете використовувати цю концепцію і для класу фрагментів)

Я намагався натиснути кнопку показати діалоговий фрагмент, натиснувши кнопку кілька разів і швидко.

            FragmentManager fm = getSupportFragmentManager();
            Fragment oldFragment = fm.findFragmentByTag("wait_modal");

            if(oldFragment != null && oldFragment.isAdded())
                return;

            if(oldFragment == null && !please_wait_modal.isAdded() && !please_wait_modal.isVisible()){
                fm.executePendingTransactions();
                please_wait_modal.show(fm,"wait_modal");
            }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.