8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

coroutineとViewModelを使ってDialogFragmentを実装する

Last updated at Posted at 2019-02-28

https://qiita.com/FKbelm/items/f85949dff7a3c0e7781d で試した内容の改善版です。

上の記事では、メモリリークを起こしていたので、ViewModelの力を使って再実装を目指しました。

ViewModelの作成

MainActivityViewModel.kt
class MainActivityViewModel() : ViewModel() {

    val hogeDialogEvent = MutableLiveData<Unit>()

    private val _hogeDialogChannel = Channel<Boolean>()
    val hogeDialogChannel = _hogeDialogChannel as SendChannel<Boolean>

    private suspend fun showHogeDialog(): Boolean {
        // ダイアログを表示
        hogeDialogEvent.postValue(Unit)

        // suspendで結果を取得
        // Channelに値が入るまで待つ
        val result = _hogeDialogChannel.receive()

        // 結果からなんかやって返す
        return TODO(result)
    }
}

showHogeDialogはActivityにダイアログを開くよう通知するhogeDialogEventに通知を出して、_hogeDialogChannel.receive()で結果が入ってくるのを待ちます。

Activityの作成

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private val viewModel by lazy {
        ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        viewModel.hogeDialogChannel.observe(this, Observer {
            // ダイアログが存在しないときに開く処理
            if (supportFragmentManager.findFragmentByTag(TAG_HOGE) == null) {
                supportFragmentManager
                    .beginTransaction()
                    .add(AlertDialogFragment(), TAG_HOGE)
                    .commitAllowingStateLoss()
            }
        })
    }
}

ActivityはDialogFragmentを開く処理のみを担っています。

DialogFragmentの作成

ChannelDialogFragment.kt
// 大元になるDialogFragment
abstract class ChannelDialogFragment<T> : DialogFragment() {
    protected abstract val channel: SendChannel<T>?
}

SendChannel<T>はcoroutinesのChannel<T>でのうち、送信機能のみのインターフェイスです。
ここから結果を送信してあげます。

AlertDialogFragment.kt
// 実際に使うDialogFragment
class AlertDialogFragment : ChannelDialogFragment<AlertDialogFragmentResult>() {

    // ActivityのViewModelを取得し、そこからchannelを取得する
    override val channel by lazy {
        activity?.let {
            ViewModelProviders.of(it).get(MainActivityViewModel::class.java).hogeDialogChannel
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val context = this.context ?: return super.onCreateDialog(savedInstanceState)

        return AlertDialog.Builder(context).apply {
            setTitle(R.string.hoge_title)
            setMessage(R.string.hoge_message)

            setPositiveButton(R.string.hoge_positive) { _, _ ->
                channel?.offer(true) // ViewModelのreceiveが受け取る
            }
            setNegativeButton(R.string.hoge_negative) { _, _ ->
                channel?.offer(false) // ViewModelのreceiveが受け取る
            }
        }.create()
    }
}

ChannelをViewModelに持たせることでActivityの破棄,再生成後にもChannelを容易に再取得できるようになりました。

Channelを直接公開せずにViewModelにインターフェイスを実装させることでも実現できそうですが、複数のDialogFragmentを持ちたいといったことがあった場合はChannelを直接扱う方が楽かなと思いました。

ViewModelの力は大きいですね。

参考

Coroutine時代のDialogFragment
https://qiita.com/idaisuke/items/b4f3c2e0a872544b97d0

Prism 5とReactiveProperty
https://blog.okazuki.jp/entry/2014/05/09/083315

Android Architecture ComponentsのViewModelとDialogFragment
http://sys1yagi.hatenablog.com/entry/2017/08/24/125416

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?