LoginSignup
2
1

More than 5 years have passed since last update.

CoroutineでDialogFragment

Last updated at Posted at 2019-02-20

coroutineとViewModelを使ってDialogFragmentを実装する
https://qiita.com/FKbelm/items/43decadf95a4360b1d64

本記事の手法をつかうと、メモリリークなどが発生しやすいので上記の記事の手法を推奨します。

もとの記事は折りたたみます

DialogFragmentはコールバックで動いているのでコルーチンで書き直したくなりました。

すでにDialogFragmentをコルーチンを使って実装する記事はありましたが、Rxを使用されていたのでkotlin coroutines標準機能のみで実現したいと思います。

Callback to suspend

コールバックで結果を受け取っているのをsuspendで実現します。

C#-Prismの例ですが、同様の処理を実現されているものがありましたのでこれをkotlinに移植します。

以下が実装例です。

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

Viewをカスタマイズせずに標準のAlertAlertDialogを使用するパターンです。

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 "なんかやる"
}

参考

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

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

2
1
1

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
2
1