我使用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
的警告。
这是对DialogFragment
s的官方建议:
注:订阅生命周期感知型组件(如
LiveData
)时,切勿使用viewLifecycleOwner
作为使用Dialogs
DialogFragment
中的LifecycleOwner
。请改用DialogFragment
本身,或者如果您使用的是喷气背包导航,请使用NavBackStackEntry
。
因此,您可以正常观察事物,但不是传递this
viewLifecycleOwner
,或者当前的backstack条目(例如findNavController().currentBackStackEntry
)。无需覆盖onCreateView
只是为了强制创建viewLifecycleOwner
或任何东西!
您的情况略有不同,但我认为概念是相同的。只需在对话框类中使用this.getActivity()
并将其作为LifeCycleOwner
传递即可。我遇到了同样的问题,因为我使用了LiveData
和Retrofit
LiveData
需要一个参考。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
不同;onCreateDialog
在onCreateView
之前被调用,所以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
}