无法使用 NavigationDrawer 设置工具栏菜单

问题描述

我正在尝试将 NavigationDrawer 与具有刷新菜单选项的工具栏结合起来:

screenshot

我遇到的问题是我无法让工具栏显示菜单按钮。

我的 MainActivity 只有一个 Fragment。 activity_main.xml

<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_nav_container"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/main" />

MainActivity.kt

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

        setContentView(R.layout.activity_main)
    }
}

nav_main

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_main"
    app:startDestination="@id/drawerFragment"
    >
    <fragment
        android:id="@+id/drawerFragment"
        android:name="com.example.rocketman.drawer.DrawerFragment"
        android:label="DrawerFragment"
        tools:layout="@layout/fragment_drawer"
        />
</navigation>

基本上我的 MainActivity 直接导航到 DrawerFragment,它在这里

<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.rocketman.drawer.DrawerFragment">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.rocketman.drawer.DrawerFragment">
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/toolbar_home"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/midnight_blue"
                app:titleTextColor="@color/white"/>
        </com.google.android.material.appbar.AppBarLayout>
        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/drawer_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    </LinearLayout>
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/drawer_nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/drawer_header"
        app:menu="@menu/drawer" />
</androidx.drawerlayout.widget.DrawerLayout>

最后,我的DrawerFragment

private const val KEY_SELECTED_DRAWER_ITEM = "DRAWER_SELECTED_ITEM_ID_KEY"

class DrawerFragment: Fragment() {

    private lateinit var binding: FragmentDrawerBinding
    private var drawerSelectedItemId = R.id.nav_home

    override fun onCreateView(
        inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
    ): View {
        binding = FragmentDrawerBinding.inflate(inflater)
        (requireActivity() as AppCompatActivity).setSupportActionBar(toolbar_home)
        return binding.root
    }

    override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
        super.onViewCreated(view,savedInstanceState)

        savedInstanceState?.let {
            drawerSelectedItemId = it.getInt(KEY_SELECTED_DRAWER_ITEM,drawerSelectedItemId)
        }

        setupDrawer()
        setBackpressedHandler()
    }

    private fun setBackpressedHandler() {
        requireActivity().onBackpresseddispatcher.addCallback(viewLifecycleOwner) {
            if (binding.drawerLayout.isOpen) {
                binding.drawerLayout.close()
            } else {
                findNavController().popBackStack()
            }
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putInt(KEY_SELECTED_DRAWER_ITEM,drawerSelectedItemId)
        super.onSaveInstanceState(outState)
    }

    private fun setupDrawer() {
        val controller = binding.drawerNavView.setupWithNavController(
            childFragmentManager,findNavController(),listof(
                //all the items on the Drawer have their own navigation graph
                R.navigation.home,R.navigation.rocket,R.navigation.company
            ),R.id.drawer_container,drawerSelectedItemId,requireActivity().intent
        )

        controller.observe(
            viewLifecycleOwner,{ navController ->
                NavigationUI.setupWithNavController(
                    binding.toolbarHome,navController,binding.drawerLayout
                )

                drawerSelectedItemId = navController.graph.id
            }
        )
    }
}

换句话说,Drawer 有三个项目:homerocketcompany,如下所示,menu/drawer

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_home"
        android:checked="true"
        android:checkable="true"
        android:icon="@drawable/ic_home"
        android:title="@string/nav_menu_home"
        />
    <item
        android:id="@+id/nav_rocket"
        android:checkable="true"
        android:icon="@drawable/ic_rocket"
        android:title="@string/nav_menu_rocket_list"
        />
    <item
        android:id="@+id/nav_company"
        android:checkable="true"
        android:icon="@drawable/ic_company"
        android:title="@string/nav_menu_company_data"
        />
</menu>

homerocketcompany 都有自己的导航图,例如 navigation/company 如下所示:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_company"
    app:startDestination="@id/companyDataFragment">
    <fragment
        android:id="@+id/companyDataFragment"
        android:name="com.example.rocketman.company.CompanyFragment"
        android:label="@string/nav_menu_company_data"
        tools:layout="@layout/fragment_company_data"
        />
</navigation>

而且 CompanyFragment 很简单:

class CompanyFragment: Fragment() {

    private lateinit var binding: FragmentCompanyDataBinding
    private val vm by lazy {
        viewmodelProvider(this).get(CompanyVM::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
        Repo.init(requireContext())
    }

    override fun onCreateView(
        inflater: LayoutInflater,savedInstanceState: Bundle?
    ): View {
        binding = FragmentCompanyDataBinding.inflate(inflater)
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu,inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu,inflater)
        inflater.inflate(R.menu.company,menu)
    }

    override fun onViewCreated(view: View,savedInstanceState)
        setupObservers()
    }

    private fun setupObservers() {
        //observing
    }
}

那部分有效。

但是,我想为 Company 创建一个菜单操作。所以首先我创建一个菜单menu/company:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/update"
        android:icon="@drawable/ic_baseline_refresh_24"
        android:title="@string/menu_company_update"
        app:showAsAction="ifRoom|withText"
        />
</menu>

然后在 CompanyFragment 中我调用 override onCreate() { setHasOptionsMenu(true) }override onCreateOptionsMenu() { inflater.inflate(R.menu.company,menu }

这应该为工具栏创建选项菜单。但是,Fragment 只显示抽屉菜单图标,而不显示选项菜单图标。

我可以向内容片段本身添加一个工具栏,但该应用程序有两个工具栏,一个带有抽屉,另一个带有选项:

screenshot

如何使工具栏同时具有 NavigationDrawer 和选项项?

解决方法

据我从您的问题中了解到,您希望显示溢出菜单和抽屉菜单。

菜单 xml 文件中的 showAsAction 选项可用于控制单个菜单选项在操作栏中的显示方式。

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/update"
        android:icon="@drawable/ic_baseline_refresh_24"
        android:title="@string/menu_company_update"
        app:showAsAction="ifRoom|withText"
        />
</menu>

改成

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/update"
        android:icon="@drawable/ic_baseline_refresh_24"
        android:title="@string/menu_company_update"
        app:showAsAction="never"
        />
</menu>

这将显示溢出菜单中的菜单项。

,

仍然有很多人继续使用ActionBar,我们不必再使用它了。如果我是你,我不会使用 ActionBar 和与 ActionBar 相关的 setHasOptionsMenu(true),而只会使用工具栏。

只需删除 setHasOptionsMenu(true)onCreateOptionsMenu()onOptionsItemSelected()(如果有)。

相反,直接在工具栏上使用 Toolbar.inflateMenu()Toolbar.setOnMenuItemClickListener()