getViewLifecycleOwner() в DialogFragment приводит к сбою
Я использую DialogFragment (onCreateDialog)
и ViewModel для него. Но, когда я пытаюсь использовать getViewLifecycleOwner() для передачи в методе наблюдаю (), я получаю ошибку, как показано ниже:
java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView().
Возможно ли использовать getViewLifecycleOwner() внутри DialogFragment?
8 ответов
Это официальная рекомендация для s:
Примечание. При подписке на компоненты с учетом жизненного цикла, такие как
LiveData
, вы никогда не должны использовать в качествеLifecycleOwner
в том, что используетDialogs
. Вместо этого используйтеDialogFragment
сам, или если вы используете Jetpack Navigation, используйтеNavBackStackEntry
.
Таким образом, вы можете просто наблюдать за вещами как обычно, но вместо того, чтобы проходить
this
, или текущая запись backstack (например,
findNavController().currentBackStackEntry
). Нет необходимости отменять
onCreateView
просто чтобы заставить
viewLifecycleOwner
быть созданным или что-нибудь!
Это происходит из-за того, как создается DialogFragment. Если вы используетеonCreateDialog()
чем для этого типа фрагментов используется несколько иной жизненный цикл. ВonCreateView()
не будет использоваться, поэтому viewLifecycleOwner
для этого фрагмента не будет инициализирован.
В качестве обходного пути вы можете использовать экземпляр Fragment в качестве владельца для наблюдателя:.observe(this, Observer {...}
. Хотя вы получите предупреждение за использованиеthis
вместо viewLifecycleOwner
.
Ваш случай немного отличается, но я думаю, что концепция такая же. Просто используйтеthis.getActivity()
в вашем Dialog Class и передайте его как LifeCycleOwner
. У меня была такая же проблема, потому что я использовалLiveData
а также Retrofit
а также LiveData
нужна ссылка. ВDialogFragment
устанавливает свои LifeCycleOwner
в какой-то момент, но этого нет ни в одном из упомянутых выше методов. ИспользуяgetActivity()
вы можете использовать своего наблюдателя уже в методе onCreateDialog. Вот некоторая часть моего кода, которая сначала вызвала некоторую проблему, когда я пытался передать ссылку на nullthis.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
- может получить доступ
viewLifecycleOwner
изнутриonCreateView
- вид вернулся из
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>
Поскольку DialogFragment появляется поверх родительского фрагмента, он может использовать его владельца жизненного цикла. Таким образом, код будет выглядеть так:
parentFragment?.viewLifecycleOwner?.let {
binding.lifecycleOwner = it
}
Существует вероятность того, что вы в первый раз возвращаете пустой экземпляр LiveData из репозитория /ViewModel. убедитесь, что вы инициировали LiveData в репозитории /ViewModel.
Возможно ли использовать getViewLifecycleOwner() внутри DialogFragment?
да
Попробуй поставить свою логику внутри onActivityCreated()
, поскольку в этот раз ваше представление не будет нулевым, что вызвало это исключение.
Также вы можете использовать getViewLifecycleOwnerLiveData()
и посредством прикрепления к нему наблюдаемой, вы можете поместить свою логику в эту наблюдаемую, которая начнется, как только lifeCycleOwner
будет инициализирован.
Мое решение было немного странным...
Мой компонент использовал getViewLifecycleOwnerLiveData() .... так:
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);
}
Поскольку FragmentViewLifecycleOwner является частным пакетом... это причина класса MyLifeCycleOwner.
Я не собирался менять свои компоненты из-за неправильного управления архитектурой Android...