J'utilise DialogFragment (onCreateDialog)
et ViewModel pour cela. Mais, lorsque j'essaie de transmettre getViewLifecycleOwner()
à la méthode LiveData::observe
, j'obtiens l'erreur ci-dessous:
java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView().
Est-il possible d'utiliser getViewLifecycleOwner()
dans un DialogFragment
?
4 Réponses :
Votre cas est légèrement différent, mais je pense que le concept est un peu le même. Utilisez simplement this.getActivity()
dans votre classe de dialogue et transmettez-le en tant que LifeCycleOwner
. J'ai eu le même problème car j'ai utilisé LiveData
et Retrofit
et LiveData
besoin d'une référence. Le DialogFragment
définit son LifeCycleOwner
à un moment donné, mais il ne correspond à aucune des méthodes mentionnées ci-dessus. En utilisant getActivity()
vous pouvez utiliser votre observateur dès la méthode onCreateDialog. Voici une partie de mon code qui a d'abord causé un problème lorsque j'ai essayé de passer un null référencé this.getViewLifecycleOwner()
au lieu de l'activité.
@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; }
Et si le fragment est détruit puis recréé, mais l'activité ne l'est pas? Les anciens observateurs font toujours référence à l'ancien fragment, et vous vous êtes fait une fuite de mémoire! Le point de vue viewLifecycleOwner
est de supprimer les observateurs lorsque le fragment est détruit.
Cela se produit en raison de la façon dont le DialogFragment est créé. Si vous utilisez onCreateDialog()
un cycle de vie légèrement différent est utilisé pour ce type de fragment. onCreateView()
ne sera pas utilisé, donc le viewLifecycleOwner
pour ce fragment ne sera pas initialisé.
: En tant que solution de contournement pour cela, vous pouvez utiliser l'instance Fragment en tant que propriétaire de l'observateur .observe(this, Observer {...}
Bien que vous obtiendrez un message d' avertissement pour l' utilisation. this
lieu du viewLifecycleOwner
.
Je ne semble recevoir aucun avertissement en utilisant this
(MyDialogFragment). Cette approche est-elle préférable à l'utilisation de getActivity()
ou de requireActivity()
?
@ ban-géoingénierie Tant que vous ne show
la DialogFragment plusieurs fois avec la même instance de fragment. Si vous le faites, les observateurs seront inscrits une deuxième fois et vous pourriez avoir des problèmes étranges en raison d'une double observation. En effet , en utilisant this
que le propriétaire du cycle de vie, les observateurs ne sont supprimés lorsque onDestroy
est appelé, et il n'est pas appelé en rejetant simplement la boîte de dialogue.
cela se produit car le cycle de vie de DialogFragment
est différent de celui de Fragment
; onCreateDialog
est appelé avant onCreateView
, donc viewLifecycleOwner
n'est pas disponible ... J'ai contourné le problème en:
onCreateView
au lieu de onCreateDialog
viewLifecycleOwner
depuis onCreateView
onCreateView
est mise dans une boîte de dialogue pour nous par DialogFragment
...code supplémentaire:
<?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>
le fichier de mise en page utilisé
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 } ... }
Ma solution était un peu farfelue ...
Mon composant utilisait le getViewLifecycleOwnerLiveData () .... donc:
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); }
Parce que FragmentViewLifecycleOwner est un package privé ... c'est la raison de la classe MyLifeCycleOwner.
Je n'allais pas changer mes composants à cause d'une mauvaise gestion de l'architecture android ...
Dans
onCreateDialog
Dialog crée pas encore créé. EssayezonViewCreated()
. Je n'ai moi-même pas beaucoup utiliséLifecycleOwner
.onViewCreated
n'est pas appelé dansDialogFragment