DialogFragment上的commitAllowingStateLoss

40

当显示一个DialogFragment时,我遇到了一个IllegalStateException错误:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

我知道为什么会发生这种情况,但我想在重写 DialogFragment 的 show 函数时使用 commitAllowingStateLoss 来显示对话框:

我明白问题的原因,但希望能够通过重写 DialogFragment 的 show 函数并使用 commitAllowingStateLoss 方法来显示对话框:

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit(); //replace it by commitAllowingStateLoss
}

但我无法访问mDismissedmShownByMe变量,我该如何访问这些变量并修改它们,就像它的父类做的那样。


1
在重写show(FragmentManage, String)时,可以忽略mDismissedmShownByMe。我不能说这样做完全安全。当mShownByMefalsemDismissed默认为false时,会有一些内部处理mDismissed的情况。但是忽略它们似乎并不会对内部处理产生影响。 - BakaWaii
4个回答

55

我认为为了防止在DialogFragment上抛出IllegalStateException,最好使用:

 YourDialogFragment dialogFragment = new YourDialogFragment();
 fragmentManager.beginTransaction().add(dialogFragment, YourDialogFragment.TAG_FRAGMENT).commitAllowingStateLoss();

不要在DialogFragment上使用show()


4
关于内部标志的更改 (mDismissed = false;mShownByMe = true;),它们不会更新吗? - Vall0n
@Vall0n 使用vuhung3990的答案,对于保存状态的情况更好。 - Wackaloon

13

原始对话片段

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit(); //replace it by commitAllowingStateLoss
}

我不知道 mDismissedmShownByMe 变量的用途,因此最好覆盖 DialogFragmentshow(FragmentManager, String) 方法,在我的情况下它可以正常工作。

override fun show(manager: FragmentManager?, tag: String?) {
    if (manager?.isDestroyed == false && !manager.isStateSaved) {
      super.show(manager, tag)
    }
  }

isStateSaved 可在 appcompat >= 26.0.0 或 androidx 中使用。


13

如果您的DialogFragment没有状态需要保存,则使用commitAllowingStateLoss解决方案可以生效,否则就像函数名所述,它们将会丢失。但我认为在大多数情况下,我们都有状态需要保存,这是DialogFragment的主要好处:Android会自动重新创建并维护其状态。

更好的解决方案是检查是否完成了重新创建过程,如果没有,则返回给调用者,该调用者可以是Activity或FragmentActivity,在其onPostResume()或onResumeFragments()回调中再次调用mark它和show函数,从而确保所有片段被重新创建。

这里是DialogFragment子类的覆盖show()方法:

public boolean show(FragmentManager fragmentManager) {
   if (fragmentManager.isStateSaved()) return false;
   show(fragmentManager, tagName);
   return true;
}

1
这是比Arash GM接受的解决方案更好的解决方案,因为它允许DialogFragment保留内部状态。 - Mygod

0
有点晚参加派对,但希望将来能帮到其他人。 出现这个问题的原因是在Activity处于最多停止状态时调用了DialogFragment.show()方法,通常是因为我们开发者试图在API调用失败或类似情况下显示对话框,并且用户将应用程序置于后台,导致在Activity停止时调用了DialogFragment.show()方法。
为了解决这个问题,我们可以做一些非常简单的事情,创建一个持有对话框实例的LiveData,然后让Activity/Fragment观察该LiveData,如果有值,就显示对话框。 现在这样做是有效的,因为LiveData只在生命周期至少处于started状态时向观察者发送事件,所以当Activity/Fragment至少处于started状态时,对话框肯定会被显示。
根据LiveData.observe的文档说明:
将给定的观察者添加到给定所有者的观察者列表中。事件在主线程上分发。如果LiveData已经设置了数据,它将传递给观察者。 只有当所有者处于Lifecycle.State.STARTED或Lifecycle.State.RESUMED状态(活动状态)时,观察者才会收到事件。
代码示例如下:
在你的Fragment/Activity中创建一个LiveData -
private val dialogLiveData = MutableLiveData<DialogFragment?>()

下面我们将观察LiveData -
片段 -
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    dialogLiveData.observe(viewLifecycleOwner) {
        if (it?.isAdded?.not() == true) {
            it.show(childFragmentManager, "dialog")
        }
    }
}

活动 -

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    dialogLiveData.observe(this) {
        if (it?.isAdded?.not() == true) {
            it.show(supportFragmentManager, "dialog")
        }
    }
}

现在当我们想要显示对话框时,我们只需要这样做 -
fun showDialog() {
    dialogLiveData.value = DialogFragment()
}

然后当我们想要关闭对话框时,我们将这样做 -

fun dismissDialog() {
    val dialog = dialogLiveData.value
    dialog?.let {
        if (dialog.isAdded) {
            if (dialog.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED).not()) {
                dialogLiveData.value?.dismissAllowingStateLoss()
            } else {
                dialogLiveData.value?.dismiss()
            }
        }
        dialogLiveData.value = null
    }
}

注意:最好将LiveData保留在ViewModel内部,否则DialogFragment的实例可能会在屏幕旋转时丢失。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接