DialogFragment 中的 getViewLifecycleOwner() 会导致崩溃



我使用DialogFragment (onCreateDialog)和ViewModel。但是,当我尝试将getViewLifecycleOwner()传递给LiveData::observe方法时,出现以下错误:

java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView().

是否可以在DialogFragment内使用getViewLifecycleOwner()

这是因为 DialogFragment 的创建方式。 如果您使用onCreateDialog()则对此类片段使用的生命周期略有不同。不会使用该onCreateView(),因此不会初始化此片段的viewLifecycleOwner

作为解决此问题的方法,您可以使用片段实例作为观察者的所有者:.observe(this, Observer {...}.尽管您将收到使用this而不是viewLifecycleOwner的警告。

这是对DialogFragments的官方建议:

注:订阅生命周期感知型组件(如LiveData)时,切勿使用viewLifecycleOwner作为使用DialogsDialogFragment中的LifecycleOwner。请改用DialogFragment本身,或者如果您使用的是喷气背包导航,请使用NavBackStackEntry

因此,您可以正常观察事物,但不是传递thisviewLifecycleOwner,或者当前的backstack条目(例如findNavController().currentBackStackEntry)。无需覆盖onCreateView只是为了强制创建viewLifecycleOwner或任何东西!

您的情况略有不同,但我认为概念是相同的。只需在对话框类中使用this.getActivity()并将其作为LifeCycleOwner传递即可。我遇到了同样的问题,因为我使用了LiveDataRetrofitLiveData需要一个参考。DialogFragment在某个时候设置其LifeCycleOwner,但它不在上述任何方法上。通过使用getActivity(),您最早可以在 onCreateDialog 方法中使用观察器。这是我代码的某些部分,当我尝试传递 null 引用this.getViewLifecycleOwner()而不是活动时,最初会导致一些问题。

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
FragmentActivity activity = this.getActivity();
binding = DialogSelectIssuesBinding.inflate(LayoutInflater.from(getContext()));
RetroRepository.
getDefault().
getAllIssues().
observe(this.getActivity(), listLiveDataResponse -> {
//ToDo Check for errors and Bind the data here 
});

AlertDialog alertDialog = new AlertDialog.Builder(activity)
.setView(binding.getRoot())
.setTitle("Please select issues from the list below:")
.setNegativeButton("CANCEL", null)
.setPositiveButton("ADD", null)
.create();
alertDialog.setCanceledOnTouchOutside(false);
return alertDialog;
}

发生这种情况是因为DialogFragment的生命周期与Fragment不同;onCreateDialogonCreateView之前被调用,所以viewLifecycleOwner不可用...我通过以下方式解决了这个问题:

  • 实现onCreateView而不是onCreateDialog
    • 可以从onCreateView内访问viewLifecycleOwner
    • onCreateView返回的视图被放入我们的对话框中,由DialogFragment...
    • 您需要在对话框中创建自己的按钮和标题...

补充代码:

class TextInputDialogFragment : DialogFragment() {
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val viewBinding = FragmentDialogTextInputBinding.inflate(layoutInflater, container, false)
val titleText = params.title.localize(requireContext())
viewBinding.toolbar.isVisible = titleText.isNotBlank()
if (titleText.isNotBlank()) {
viewBinding.toolbar.title = titleText
}
viewBinding.recyclerview.adapter = ListItemAdapter(
viewLifecycleOwner, requireContext().app.nowFactory, viewModel.fields
)
viewBinding.buttonAffirm.setOnClickListener {
listener.onOkPressed(viewModel.userInputtedText.value)
dismiss()
}
viewBinding.buttonReject.setOnClickListener {
dismiss()
}
viewModel.enablePositiveButton.observe(viewLifecycleOwner) { isEnabled ->
viewBinding.buttonAffirm.isEnabled = isEnabled
}
return viewBinding.root
}
...
}

使用的布局文件

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
tools:title="Title" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<LinearLayout
style="?buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:gravity="end"
android:orientation="horizontal"
android:padding="@dimen/min_touch_target_spacing_half">
<Button
android:id="@+id/button_reject"
style="?buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/min_touch_target_spacing_half"
android:text="@android:string/cancel" />
<Button
android:id="@+id/button_affirm"
style="?buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/min_touch_target_spacing_half"
android:text="@android:string/ok" />
</LinearLayout>
</LinearLayout>
</layout>

我的解决方案有点古怪...

我的组件正在使用getViewLifecycle OwnerLiveData() ....所以:

private final MyLifeCycleOwner owner = new MyLifeCycleOwner();
private final MutableLiveData<LifecycleOwner> result = new MutableLiveData<>();
@NonNull
@Override
public LiveData<LifecycleOwner> getViewLifecycleOwnerLiveData() {
return result;
}
@Override
public void onDestroyView() {
super.onDestroyView();
owner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
result.setValue(null);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
result.setValue(owner);
owner.getLifecycle();
owner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
return super.onCreateView(inflater, container, savedInstanceState);
}

因为 FragmentViewLifecycle Owner 是包私有的...这就是MyLifeCycleOwner类的原因。

由于对 android 架构的一些管理不善,我不打算更改我的组件......

由于DialogFragment弹出父片段的视图,因此可以使用其生命周期所有者。因此,代码将如下所示:

parentFragment?.viewLifecycleOwner?.let {
binding.lifecycleOwner = it
}

相关内容

  • 没有找到相关文章

最新更新