編集画面で保存せずに戻るときに確認ダイアログを出したい

ユーザー入力を伴う画面で戻るボタンをクリックしたとき、編集内容が消えてしまうのを通知して、 ユーザーに本当に戻るか、画面にとどまるかを選択させたい。

確認ダイアログ

デフォルトでは戻るボタンは問答無用に前の画面に戻るので、処理を止めつつダイアログを出す必要がある。

ホームボタンクリック時の処理を変える

Fragment#onOptionsItemSelected()をオーバライドすることでホームボタンクリック時の処理を変更できる。

主な流れは以下の通り。

  1. setHasOptionMenu(true)でメニューボタンのハンドリングを有効化する
  2. Fragment#onOptionsItemSelected()をオーバライドする
  3. 実装してホームボタンクリック時(android.R.id.home)で確認ダイアログを出す
  4. 実装してホームボタンクリック時(android.R.id.home)でtrueを返す

参考: 【Android,Kotlin】onOptionsItemSelectedでtrueを返すとホームボタンが反応しなくなる

実装

このような場合はホームボタンをクリックしたときはダイアログを出すだけで画面移動はしないようにしたい。
そしてOKボタンをクリックしたときのみ前の画面へ戻るようにしたい。

以下のようにホームボタンクリック時にtrueを返せば実現できる。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setHasOptionsMenu(true)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        android.R.id.home -> {
            val context = context ?: return false
            MaterialAlertDialogBuilder(context).apply {
                setMessage("編集内容が破棄されますがよろしいですか?")
                setPositiveButton("OK") { _, _ ->
                  findNavController().popBackStack() // OKをクリックしたときに戻る
                }
                setNegativeButton("キャンセル") { _, _ -> }
            }.show()
            true // ← true を返すことで戻らなくなる
        }
        else -> super.onOptionsItemSelected(item)
    }
}

実装(MenuProvider)

MenuProvider利用時も考え方は同じ。

onMenuItemSelectedtrueを返しつつダイアログを表示する。

activity?.addMenuProvider(
    object : MenuProvider {
        override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
            inflater.inflate(R.menu.menu_main, menu)
        }

        override fun onMenuItemSelected(item: MenuItem): Boolean {
            return when (item.itemId) {
                android.R.id.home -> {
                  val context = context ?: return false
                  MaterialAlertDialogBuilder(context).apply {
                      setMessage("編集内容が破棄されますがよろしいですか?")
                      setPositiveButton("OK") { _, _ ->
                        findNavController().popBackStack() // OKをクリックしたときに戻る
                      }
                      setNegativeButton("キャンセル") { _, _ -> }
                  }.show()
                  true // true を返すと戻らなくなる
                }
                else -> false
            }
        }
    },
    viewLifecycleOwner,
    Lifecycle.State.RESUMED
)

OSのバックボタンでも確認ダイアログを出す

Android OSには標準のバックボタンもあるので、そちらの対処も必要となる。

Android OS標準のバックボタンにフックして実行するにはActivity#onBackPressedDispatcherにコールバックを登録する。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    activity?.onBackPressedDispatcher?.addCallback(this) {
        val context = context ?: return@addCallback
        MaterialAlertDialogBuilder(context).apply {
            setMessage("編集内容が破棄されますがよろしいですか?")
            setPositiveButton("OK") { _, _ ->
              findNavController().popBackStack() // OKをクリックしたときに戻る
            }
            setNegativeButton("キャンセル") { _, _ -> }
        }.show()        
    }
}