Activity や Fragment で Menu を表示させる API に MenuHost と MenuProvider の新しい API が登場しました。
Fragment 1.5.0-alpha05 で setHasOptionsMenu() が deprecated になり、この新しい API への移行が案内されていたので、新しい API について書いていこうと思います。
-
androidx.activity:activity(orandroidx.activity:activity-ktx) のバージョンが 1.4.0 以降 -
androidx.core:core(orandroidx.core:core-ktx) のバージョンが 1.7.0 以降
である必要があります。
Activity での実装
これまでの実装
onCreateOptionsMenu と onOptionsItemSelected を override して Menu のリソース指定とタップ時の処理を行なっていました。
class ExampleActivity : AppCompatActivity(R.layout.activity_example) {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.settings -> {
openSettings()
true
}
R.id.help -> {
showHelp()
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
新しい API での実装
addMenuProvider に MenuProvider をセットして、その MenuProvider の中で Menu のリソース指定とタップ時の処理を行うようになります。
class ExampleActivity : AppCompatActivity(R.layout.activity_example) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_main, menu)
}
override fun onMenuItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.settings -> {
openSettings()
}
R.id.help -> {
showHelp()
}
}
return true
}
})
}
}
Fragment での実装
これまでの実装
Fragment も onCreateOptionsMenu と onOptionsItemSelected を override して menu のリソース指定とタップ時の処理を行なっていました。
また Fragment の場合はこれに加えて setHasOptionsMenu(true) を呼ぶ必要がありました。
class ExampleFragment : Fragment(R.layout.fragment_example) {
override fun onCreate(savedInstanceState: Bundle?) {
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_example, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.settings -> {
openSettings()
true
}
R.id.help -> {
showHelp()
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
新しい API での実装
新しい API の場合は Activity に実装されている MenuHost に対して addMenuProvider で MenuProvider と Fragment のライフサイクルをセットして、その MenuProvider の中で Menu のリソース指定とタップ時の処理を行うようになります。
setHasOptionsMenu(true) を呼ぶ必要もなくなります。
class ExampleFragment : Fragment(R.layout.fragment_example) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_example, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
when (item.itemId) {
R.id.settings -> {
openSettings()
}
R.id.help -> {
showHelp()
}
}
return true
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
}
移行後の注意点
FragmentPagerAdapter で表示されている Fragment によって Menu を出し分けている場合は FragmentPagerAdapter に BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT を指定する必要があります。
- Fragment のライフサイクルで Menu の表示を出し分けている関係で、
FragmentPagerAdapterで表示されている Fragment のみ resume される状態にしないと、他画面の Menu も合わさって表示されることになる - デフォルトは
BEHAVIOR_SET_USER_VISIBLE_HINTなのでBEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTに変えるとライフサイクルの挙動がずれるから指定するときは要確認