coroutineとViewModelを使ってDialogFragmentを実装する
https://qiita.com/FKbelm/items/43decadf95a4360b1d64
本記事の手法をつかうと、メモリリークなどが発生しやすいので上記の記事の手法を推奨します。
DialogFragmentはコールバックで動いているのでコルーチンで書き直したくなりました。 すでにDialogFragmentをコルーチンを使って実装する記事はありましたが、Rxを使用されていたのでkotlin coroutines標準機能のみで実現したいと思います。 コールバックで結果を受け取っているのをsuspendで実現します。 C#-Prismの例ですが、同様の処理を実現されているものがありましたのでこれをkotlinに移植します。 以下が実装例です。 Viewをカスタマイズせずに標準のAlertAlertDialogを使用するパターンです。 Coroutine時代のDialogFragment Prism 5とReactivePropertyもとの記事は折りたたみます
Callback to suspend
abstract class BaseDialogFragment<T> : DialogFragment() {
private val channel = Channel<T>()
protected fun sendResult(result: T) = channel.offer(result)
suspend fun showAndReceive(fm: FragmentManager, tag: String? = null): T {
show(fm, tag)
return channel.receive()
}
suspend fun receive(): T = channel.receive()
}
BaseDialogFragment<T>
を継承したクラスで適切にsendResult
することで、呼び出し側でreceive()
すれば結果を取り出せます。showAndReceive
はなくても大丈夫ですが、便利そうなので追加しました。
C#との対応
C#
kotlin
await task
suspendFunction()
TaskCompletionSource<T>
Channel<T>
TaskCompletionSource<T>.SetResult
Channel<T>.send
Channel<T>.offer
await TaskCompletionSource<T>.Task
Channel<T>.receive
使用例
DefaultAlertDialogFragment
class DefaultAlertDialogFragment : BaseDialogFragment<Boolean?>() {
// キャンセル時に結果を返さないとreceiveが終わらない
override fun onCancel(dialog: DialogInterface?) {
super.onCancel(dialog)
sendResult(null)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = AlertDialog.Builder(context!!).apply {
arguments!!.also { args ->
setTitle(args.getString(ARGS_TITLE))
setMessage(args.getString(ARGS_MESSAGE))
args.getString(ARGS_POSITIVE)?.let {
setPositiveButton(it) { _, _ ->
sendResult(true)
}
}
args.getString(ARGS_NEGATIVE)?.let {
setNegativeButton(it) { _, _ ->
sendResult(false)
}
}
args.getString(ARGS_NEUTRAL)?.let {
setNeutralButton(it) { _, _ ->
sendResult(null)
}
}
}
}.create()
companion object {
const val ARGS_TITLE = "title"
const val ARGS_MESSAGE = "message"
const val ARGS_POSITIVE = "positive"
const val ARGS_NEGATIVE = "negative"
const val ARGS_NEUTRAL = "neutral"
@JvmStatic
fun newInstance(
title: String? = null,
message: String? = null,
positiveButtonText: String? = null,
negativeButtonText: String? = null,
neutralButtonText: String? = null
) = DefaultAlertDialogFragment().apply {
arguments = Bundle().apply {
putString(ARGS_TITLE, title)
putString(ARGS_MESSAGE, message)
putString(ARGS_POSITIVE, positiveButtonText)
putString(ARGS_NEGATIVE, negativeButtonText)
putString(ARGS_NEUTRAL, neutralButtonText)
}
}
}
}
val alertDialogResult = DefaultAlertDialogFragment.newInstance(
message = getString(R.string.message),
positiveButtonText = getString(R.string.btn_yes),
negativeButtonText = getString(R.string.btn_no)
).showAndReceive(supportFragmentManager)
if (finishDialogResult == true) {
return "なんかやる"
}
参考
https://qiita.com/idaisuke/items/b4f3c2e0a872544b97d0
https://blog.okazuki.jp/entry/2014/05/09/083315