Mokelab Blog

#38 2020年版、Androidでバックボタンを押したときの動作を変える方法

Androidで、バックボタンを押した時(戻るジェスチャーをした時)は、1つ前のフラグメントや アクティビティに戻ります。今回はその処理を変更する方法を紹介します。

今までの方法

バックボタンを押した時はActivityonBackPressed() が呼ばれます。今まではこのメソッドをオーバーライドし、バックボタンの処理を行いたいフラグメントなどに イベントをなんとかして通知する方法を各自作っていました。

2020年7月時点での方法

FragmentActivityAppCompatActivityに、 OnBackPressedDispatcherが追加されました。このオブジェクトに対し、コールバックを渡すことで バックボタンを押したときの処理を変更することができます。

フラグメントで使用する例です。トーストを表示しつつ、1つ前の画面に戻ります。

class MainFragment : Fragment(R.layout.fragment_main) {

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

        requireActivity().onBackPressedDispatcher.addCallback(this) {
            Toast.makeText(
                requireContext(),
                "Back pressed",
                Toast.LENGTH_LONG
            ).show()
            findNavController().popBackStack()
        }
    }
} 

addCallback(this, {})activity-ktxの拡張関数なので注意しましょう(Android Studioの候補に出てこなくて焦りました)。 第1引数でLifecycleOwnerを渡すことで、コールバックを有効にするタイミングと、removeするタイミングを制御しています。

Navigation UIとセットで使う場合

NavigationUIを使うと、ナビゲーションの状態とアクションバー/ツールバーの状態をあわせることができます。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        // FragmentContainerViewを使ってる場合は
        // ここに書かないとNavControllerが見つからない。。。
        NavigationUI.setupActionBarWithNavController(
            this, findNavController(R.id.fragment))
    }
}

このようにアクションバーとセットで使う場合、onSupportNavigateUp()を実装する必要があります。 NavigationUIが登場した時のサンプルだと、次のように実装していたと思います。

override fun onSupportNavigateUp()
  = findNavController(R.id.fragment).navigateUp()

しかし、いきなりnavigateUp()を呼ぶと、アクションバーにある戻るボタン(←)タップ時、セットしたコールバックを無視して戻る処理が行われます。 フラグメント側でセットしたコールバックが呼ばれるようにするには、次のようにします。

override fun onSupportNavigateUp(): Boolean {
    if (onBackPressedDispatcher.hasEnabledCallbacks()) {
        onBackPressedDispatcher.onBackPressed()
    }
    return true
}

これで、アクションバーにある戻るボタンタップ時もコールバックが呼ばれるようになります。

まとめ

onBackPressedDispatcherを使ったバック時の処理の書き方を説明しました。 戻る前に保存するかどうかを聞くことは多いと思うので、使ってみましょう。

コラム:「アプリを終了しますか?」

今回紹介した方法を使うと、ルートとなるフラグメントで「アプリを終了しますか?」と確認することもできてしまいます。 ユーザーは「アプリを終了させたい」からバックボタンを押しているのに、そこで確認が入ることは操作性を著しく低下させてしまいます。 Androidに標準で入っているアプリのほぼすべてで、「アプリを終了しますか?」の確認を行っていない動作にあわせ、操作性を低下させないようにしましょう。

例外はゲーム系アプリといった「アプリの起動にとても時間のかかるもの」でしょうか。。。

本サイトではサービス向上のため、Google Analyticsを導入しています。分析にはCookieを利用しています。