22
12

More than 1 year has passed since last update.

Menuの表示処理をMenuHostとMenuProviderに置き換える

Posted at

Activity や Fragment で Menu を表示させる API に MenuHostMenuProvider の新しい API が登場しました。
Fragment 1.5.0-alpha05setHasOptionsMenu() が deprecated になり、この新しい API への移行が案内されていたので、新しい API について書いていこうと思います。
  

  • androidx.activity:activity(or androidx.activity:activity-ktx) のバージョンが 1.4.0 以降
  • androidx.core:core (or androidx.core:core-ktx ) のバージョンが 1.7.0 以降

である必要があります。

Activity での実装

これまでの実装

onCreateOptionsMenuonOptionsItemSelected を 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 での実装

addMenuProviderMenuProvider をセットして、その 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 も onCreateOptionsMenuonOptionsItemSelected を 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 に対して addMenuProviderMenuProvider と 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 を出し分けている場合は FragmentPagerAdapterBEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT を指定する必要があります。

  • Fragment のライフサイクルで Menu の表示を出し分けている関係で、FragmentPagerAdapter で表示されている Fragment のみ resume される状態にしないと、他画面の Menu も合わさって表示されることになる
  • デフォルトは BEHAVIOR_SET_USER_VISIBLE_HINT なので BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT に変えるとライフサイクルの挙動がずれるから指定するときは要確認
22
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
12