ViewModelからFragmentへ画面遷移イベントを送りたい

MVVMパターンで設計していると、onClickなどのイベントをViewModelで受けて、そこから画面を遷移させたいケースがよくあります。画面遷移はFragment(またはActivity)で実装しますから、何らかの形でViewModelからFragmentへ通知する必要があります。LiveDataとObserverを使う方法もありますが、Googleのリポジトリで公開されているEventクラスが便利です。

ViewModelでEventクラスを使ってイベントを定義し、FragmentでEventObserverクラスを使ってイベントを監視します。EventObserverクラスはObserverクラスを継承しているので、FragmentやActivityのライフサイクルが終了すると自動で登録解除され、メモリリークすることがありません。Eventに値を設定すると、EventObserverのonChanged()メソッドが呼び出されます。

class SampleViewModel: ViewModel() {
    val onTransit = MutableLiveData<Event<String>>()

    fun onClickButton() {
        onTransit.value = Event("onTransit")
    }
}

class SampleFragment: Fragment() {
    private val viewModel: SampleViewModel by viewModel()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.onTransit.observe(viewLifecycleOwner, EventObserver {
            findNavController().navigate(R.id.action_sampleFragment_to_nextFragment)
        })
    }
}

通常のLiveData/Observerと異なるのは、onChanged()メソッドが呼び出されるのが一度だけという点です。onChanged()メソッドに画面遷移を実装すると、ViewModelからFragmentにイベントを通知して画面遷移するという流れをシンプルに実装できます。

では、通常のLiveData/Observerを使うとどうなるでしょうか。LiveDataに値を設定するとObserverのonChanged()が呼ばれ、画面が遷移します。ここまでは良いのですが、問題は遷移先の画面から元の画面に戻った時です。元の画面のFragmentのonViewCreated()でObserverを登録すると、LiveDataには値が設定されたままですので、即座にonChanged()が呼ばれ、また画面遷移が発生してしまい、結果として元の画面に戻ることができなくなります。これを防ぐために、一度だけ遷移するというようなフラグを実装する羽目になります。これをクラス内部で実現してくれているのが、Event/EventObserverというわけです。

「ViewModelからFragmentへ画面遷移イベントを送りたい」への1件のフィードバック

  1. ピンバック: RecyclerViewとCoroutineで非同期フォトギャラリーを作る - 縁側プログラミング

コメントは受け付けていません。