如何在Android中使用导航抽屉设置导航组件?

22

我该如何使用导航组件和导航抽屉来设置应用程序导航?

是否可以在一个Activity中完成所有操作?

在只有一个Activity和动态工具栏可见性的片段的情况下,我该如何处理工具栏的可见性?此外,还有一些片段需要关闭抽屉并使其无法访问。

这是一个自答问题,并且更像是教程而不是实际的问答。

1个回答

29
如何在导航抽屉中设置导航组件?如何在我的应用程序中使用它?
当使用 Navigation 组件时,导航抽屉的设置会有所不同。
请注意,如果您创建了一个带有抽屉导航的新应用程序,则当前教程不需要。但是,我将解释一些可能看起来奇怪的事情,如果您决定在应用程序的后期添加一个抽屉,则可参考本教程。
首先,您需要设置您的 activity_main.xml 和 MainActivity,使其准备好使用 Navigation Architecture:
<?xml version="1.0" encoding="utf-8"?>
<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:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>

其中app_bar_main只是:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

content_main是您的片段将被保存的地方:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/app_bar_main">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

需要知道的事情:在AndroidManifest.xml中,该活动不能设置 AppBar:

android:theme="@style/AppTheme.NoActionBar"

如果您在NavigationView标签中看到app:menu="@menu/activity_main_drawer",则片段名称应与它们在mobile_navigation.xml中的名称相同:

<group android:checkableBehavior="single">
        <item
            android:id="@+id/homeFragment"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/galleryFragment"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/slideshowFragment"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/toolsFragment"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/menu_tools" />
    </group>

    <item android:title="Communicate">
        <menu>
            <item
                android:id="@+id/shareFragment"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/menu_share" />
            <item
                android:id="@+id/sendFragment"
                android:icon="@drawable/ic_menu_send"
                android:title="@string/menu_send" />
        </menu>
    </item>

</menu>

下面将要讲解的内容将无需调用 onCreateOptionsMenu 来检测点击事件。Android团队已经为我们解决了这个问题。请按照以下步骤进行操作。

到目前为止,这与我们通常设置抽屉并没有太大区别。但是,在应用程序逻辑部分,我们需要进行一些配置。因此,让我们打开 MainActivity.kt。首先需要使用以下代码:

  private var appBarConfiguration: AppBarConfiguration? = null
  private var drawerLayout: DrawerLayout? = null
  private var toolbar: Toolbar? = null
  private var navController: NavController? = null

在您的onCreate方法中:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar) //set the toolbar

        drawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        navController = findNavController(R.id.nav_host_fragment)
        appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.homeFragment,
                R.id.galleryFragment,
                R.id.slideShowFragment,
                R.id.toolsFragment,
                R.id.shareFragment,
                R.id.sendFragment,
                R.id.loginFragment,
                R.id.phoneConfirmationFragment
            ), drawerLayout
        )

        setupActionBarWithNavController(navController!!, appBarConfiguration!!) //the most important part
        navView.setupWithNavController(navController!!) //the second most important part

      //other things unrelated
    }

让我们看看这里发生了什么:

首先,您需要引用navControllerAppBarConfiguration只是一个保存将作为顶级目标打开的片段的类。这意味着在它们之后将要打开的片段将释放当前片段从片段回退栈中。重要的是要告诉AppBarConfiguration我们也有一个抽屉(作为构造函数中的参数传递)。

在下面,您将看到一个名为onSupportNavigateUp()的方法:

override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration!!) || super.onSupportNavigateUp()
    }

这个方法与返回和后退按钮有关。但是,如果您使用抽屉式导航,则不太需要它。当您在返回堆栈上添加了很多片段(或至少两个)时,这确实非常方便。

所有的东西都可以在一个Activity中完成吗?

是的,当然!但是,在条件导航方面还需要更多的工作。例如,当您想要显示不属于您的抽屉应用程序的片段时。但是谷歌在这方面已经取得了巨大的进展。您可以在此处参考条件导航

如何处理仅使用一个Activity和具有动态工具栏可见性的片段,同时还有需要关闭抽屉并使其无法访问的片段的工具栏可见性?

您可以使用navController中的addOnDestinationChangedListener

navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.loginFragment, R.id.registerFragment, R.id.phoneConfirmationFragment -> {
                    toolbar?.visibility = View.GONE
                    drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
                }
                else -> {
                    toolbar?.visibility = View.VISIBLE
                    drawerLayout?.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
                }
            }
        }

现在你的应用程序上有了抽屉和导航组件。


1
嗨,我按照你的步骤做了一遍,甚至看到了汉堡图标,但是在点击它时,导航抽屉并没有打开。我可能错过了什么吗? - dazzieta
1
点击时请检查,isOpened/isClosed属性可能存在问题。 - coroutineDispatcher
1
点击事件被捕获在onOptionsItemSelected中,因为我返回的是true而不是super.onOptionsItemSelected(item)。因此,点击事件没有任何反应。无论如何,谢谢你的回答,帮了我很多。 - dazzieta
1
setupActionBarWithNavController(navController!!, appBarConfiguration!!) this method doesn't work anymore, need to pass context like setupActionBarWithNavController(this,navController!!, appBarConfiguration!!) - Tausif
3
我正在开发一个应用程序。我希望在单击2个导航抽屉菜单时显示Toast,并在另一个菜单中打开片段。我该如何处理这个问题? - Rajneesh Shukla
我们是否仍需要使用导航组件处理后退按钮点击时抽屉关闭的情况? - BabyishTank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接