ActionBarをMenuHostHelperに対応させる
私の過去記事でActcionBarのサンプルを投稿しましたが、Activityにか対応していませんでした。
ActionBarをカスタマイズしてみる
Fragmentに対応させるのと当時に、ActcionBarの制御方法がMenuHostHelperに変わっているので、その対応も同時に紹介します。
MenuHostHelperについては公式でもあまり詳しく説明されておらず、androidx.activity:activity:1.4.0-alpha01の変更点として説明されています。
そして、今は公式ではActionBarよりもToolBarを使用することを推奨していて、説明もToolbarに変わっています。ToolBarの説明は別途として、今回は従来通りのActionBarをMenuHostHelperに対応させる説明をします。
MainActivity
メニューの定義ファイルの作り方は前回と同じです。MainActivity.kt側の対応は、以下の様になります。わかりやすいように関数を分けました。
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) .apply {
setContentView(this.root)
}
supportActionBar?.setTitle("あー、ほげほげ")
supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.GREEN))
setupMenuBar()
}
private fun setupMenuBar() {
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_sample, menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.iconItem1 -> ・・・
else -> {}
}
return true
}
})
}
}
ミソは
addMenuProvider(object : MenuProvider { ・・・
でこのaddMenuProviderはComponentActivityの中で、MenuHostHelper#addMenuProviderを呼び出しています。
関数、onCreateMenuinflateして、onMenuItemSelectedでActionBarのアイコン、またはメニューがタップされた場合の処理を書きます。
Fragment側の対応
おそらく、Activity1個に対し複数Fragmnent方式でくるくる画面を入れ替えて遷移するような方式だとActionBarは全てのFragmnentで共有したくなると思います。一部、特定のFragmentによってはActionBarの特定のアイコン、メニューを非表示にしたくなると思います。(タップさせない)
その場合、最大公約数的に、メニュー定義ファイルは1個で全てを記述し、MainActivityに処理を集約します。各Fragmentは違うところだけ書きます。
class Fragment1 : Fragment() {
private lateinit var binding: Fragment1Binding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = Fragment1Binding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupMenuBar()
・・・
}
private fun setupMenuBar() {
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
// 空
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.iconItem1).isVisible = false // アイコン
menu.findItem(R.id.menu_item3_1).isVisible = false // メニュー
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
// 空
return true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
}
わかりやすいように関数を分けました。Activityの場合とちょっと違います。
val menuHost: MenuHost = requireActivity()
MenuHostはActivityのMenuHostを取得しています。addMenuProviderはActivityと引数が違います。LifecycleOwnerと、Lifecycle.Stateが必要です。
fun addMenuProvider(
provider: MenuProvider,
owner: LifecycleOwner,
state: Lifecycle.State
): Unit
onCreateMenu関数は空です。Activityではここでメニュー定義ファイルをinflateしていましたが、FragmentでもinflateするとActionBarのアイコン、メニューが2倍になります。
onMenuItemSelected関数も空でtrueだけ返せばいいです。ActionBarのアイコン、メニューがタップされた場合のハンドリングは全てMainActivityでやります。
あと、MainActivityになかったのが、onPrepareMenu関数。
ここではこのFragmentに特化して表示したくない、ActionBarのアイコン、メニューがある場合、そのisVisible を false にします。
Fragmentが他に複数ある場合も同様です。
最後に
ソースはgitHubにpushしました。