見出し画像

NavigationとFragmentとToolbarと

前置き

AndroidにはToolbarと呼ばれる部分があります。(SnazzyAppと書かれた部分です。)

画像1

ここにはハンバーガメニューだったり、アプリ名だったり、メニューだったりと、様々な情報を表示するのに使います。
そして、このtoolbarを扱うAPIはActivityに生えています。

Jetpack composeのNavigationの登場により、Fragmentを遷移させて画面遷移を実現させる方法が増えてきたと思います。画面によって、toolbarの中身を変えたい。画面によってはtoolbarを消したい。と言ったニーズが私の中で出てきました。今回はその解決方法を考えてみました。

解決方法

・ToolbarはActivityに置く
・MenuはFragmentで管理する
・Activityが現在の画面を認識するためにfindNavController().addOnDestinationChangedListenerを使用し、その中でtoolbarを出したり消したりする

ToolbarはActivityに置く

ActivityにはNavHostFragmentのコンテナfragmentがあると思います。一緒にToolbarも置いておきます。そして、toolbarに機能を追加するために、setSupportActionBar()を呼びます。ここまでは普通のtoolbarと同じです。

MenuはFragmentで管理する

FragmentでtoolbarのMenuを管理するためには、setHasOptionsMenu(true)を呼びます。そうすると、FragmentのメソッドであるonCreateOptionsMenu()が呼ばれるので、inflater.inflate(R.menu.hoge, menu)で設定してください。設定されたmenuが選択されたら、onOptionsItemSelected()が呼ばれます。この辺りはActivityの場合と同じです。

class HogeFragment: Fragment() {

    override fun onCreateView(
       inflater: LayoutInflater, container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View? {
        setHasOptionsMenu(true)
        // 以下略
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        val inflater: MenuInflater = menuInflater
        inflater.inflate(R.menu.game_menu, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle item selection
        return when (item.itemId) {
            R.id.new_game -> {
                newGame()
                true
            }
            R.id.help -> {
                showHelp()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

 

Activityが現在の画面を認識するためには

Navigationで画面遷移を管理していると、遷移した時のイベントを拾うことができます。findNavController().addOnDestinationChangedListenerで遷移先のdestinationを拾うことができるので、navigation_graph.xmlのdestination_idと等しいものに遷移したことになります。
ここまで分かれば、toolbarが不要なfragmentに遷移したらtoolbar.visibility = View.GONEとすれば良いし、必要ならtoolbar.visibility = View.VISIBLEとすればokです。

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

        this.nav_fragment.findNavController().addOnDestinationChangedListener { _, destination, _ ->
            when(deestination.id) {
                R.id.hogeFragment -> {}
            }
        }
    }
}

検討したけど採用しなかった方法

fragmentからactivityを操作する
fragmentからactivityを操作して、toolbarをsetSupportActionBar()に登録する方法も考えました。
ですが、activityがtoolbarを持っていることをfragmentが知っているのは依存の方向性に反している思います。

まとめ

最初はFragmentにtoolbarを持たせようかと思っていましたが、茨の道っぽいのでやめました。
setSupportActionBarを呼ばないならtoolbarを使うメリットはほとんどありません。
toolbarはactivityで管理して、toolbarに表示するMenuはFragmentで管理することで目的は達成できました

この記事が気に入ったらサポートをしてみませんか?