IllegalStateException:片段已添加到 tabhost 片段中

问题描述 投票:0回答:16
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 构建的 Android 应用程序。总共三个选项卡,在tab2中,有一个按钮可以在tab2中进行fragment交易(也就是调用fragment Activity中的函数)

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

如果我这样跑的话会有例外:

  1. 在tab2内,我按下按钮来更改片段
  2. 转到其他选项卡(例如选项卡 1 或选项卡 3)
  3. 按返回按钮
  4. 抛出异常

如何解决这个问题?感谢您的帮助

android android-fragments tabs android-lifecycle fragment-tab-host
16个回答
184
投票

当我们尝试在关闭之前添加相同的片段或 DialogFragment 两次时,就会发生这种情况,

只需致电

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

话虽如此,我不明白为什么要删除旧片段并再次添加相同的片段,因为我们可以通过简单地将参数传递给片段内的方法来更新 UI/数据


17
投票

删除旧片段(如果仍然添加),然后添加新片段:

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

11
投票

您只需检查下面提到的片段中的一个条件:

if(!isAdded())
{
    return;
}

isAdded = 如果片段当前已添加到其 Activity 中,则返回 true。 取自官方文档。 如果已经添加了该片段,则不会添加该片段

查看以下链接以获取参考:
http://developer.android.com/reference/android/app/Fragment.html#isAdded()


8
投票

有时会发生这种情况,因为没有从相应的布局中找到正确的 id。我遇到了这个问题。然后几个小时后我发现我设置了错误的recyclerview id。我改变了它,对我来说效果很好。

因此,请仔细检查您的片段布局。


7
投票

您只需在开始片段交易前检查一个条件

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

这对我来说非常有效......


1
投票

你可以试试这个:

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

1
投票

当我没有将我的正文 XML 包装在 FrameLayout 内的 ViewGroup 中时,出现此错误。

错误:

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

希望这可以帮助别人。


1
投票

这是我的对话框片段解决方案(您也可以将这个概念用于片段类)

我尝试通过多次点击按钮快速单击显示对话框片段。

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

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>
是选项卡中的片段列表)。


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

0
投票

当我在 Fragment 中错误地使用 ViewModel 时,出现此错误,如下所示:

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

正确做法:

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

0
投票

不好解决,但它有效)

if(!ConfirmDataSync.isVisible) show()


0
投票

碎片交易是异步的。

在执行片段事务之前,您可能会两次或多次调用此代码。

!selectPlan04Dialog.isVisible() & !selectPlan04Dialog.isAdded()
条件是
true
并且
show()
安排另一个片段事务稍后执行。

解决此问题的一些选项:

  • 每次创建一个新对话框,不要尝试重复使用旧对话框
  • 将异步片段事务更改为与片段管理器调用同步
    executePendingTransactions()
  • С都这样
    videoOptionDialog.show(supportFragmentManager.beginTransaction().remove(videoOptionDialog),TrainingVideoMoreOptionDialog.TAG)
    

0
投票

最好使用

commitNow()
进行交易。请参阅文档。

/**
     * After a {@link FragmentTransaction} is committed with
     * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
     * is scheduled to be executed asynchronously on the process's main thread.
     * If you want to immediately executing any such pending operations, you
     * can call this function (only from the main thread) to do so.  Note that
     * all callbacks and other related behavior will be done from within this
     * call, so be careful about where this is called from.
     *
     * <p>If you are committing a single transaction that does not modify the
     * fragment back stack, strongly consider using
     * {@link FragmentTransaction#commitNow()} instead. This can help avoid
     * unwanted side effects when other code in your app has pending committed
     * transactions that expect different timing.</p>
     * <p>
     * This also forces the start of any postponed Transactions where
     * {@link Fragment#postponeEnterTransition()} has been called.
     *
     * @return Returns true if there were any pending transactions to be
     * executed.
     */
© www.soinside.com 2019 - 2024. All rights reserved.