带有导航抽屉的导航组件

5
我正在尝试在我的项目中使用Navigation Component实现NavigationDrawer。我有一个作为入口点的MainActivity和多个fragment目标。我已经将我的抽屉菜单项链接到nav_graph.xml中的目标,一切都很顺利。
我的NavigationDrawer中有一个“注销”菜单项选项,当点击它时,我会启动LoginActivity。我的问题是:
  1. 我将LoginActivity添加到了我的nav_graph中,并将其链接到抽屉中的注销菜单项,这样做是否正确?
  2. 一旦启动LoginActivity,当用户按系统返回按钮时,我希望应用程序回到启动屏幕而不是堆栈中的上一个目标。使用Navigation Component可以实现这一点吗?
以下是我的nav_graph.xml:
<?xml version="1.0" encoding="utf-8"?>
<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_graph"
    app:startDestination="@id/dashboardFragment">

    <fragment
        android:id="@+id/dashboardFragment"
        android:name="com.example.fragment.DashboardFragment"
        android:label="DashboardFragment"
        tools:layout="@layout/fragment_dashboard" />
    <fragment
        android:id="@+id/profileFragment"
        android:name="com.example.fragment.ProfileFragment"
        android:label="ProfileFragment"
        tools:layout="@layout/fragment_profile" />
    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.example.fragment.SettingsFragment"
        android:label="SettingsFragment"
        tools:layout="@layout/fragment_sale_edit" />
    <activity
        android:id="@+id/loginActivity"
        android:name="com.example.activity.LogoutActivity"
        android:label="LogoutActivity"></activity>
</navigation>

这是我的drawer_menu.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigationView">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/dashboardFragment"
            android:icon="@drawable/ic_menu_camera"
            android:title="Dashboard" />
        <item
            android:id="@+id/profileFragment"
            android:icon="@drawable/ic_menu_gallery"
            android:title="Profile" />
        <item
            android:id="@+id/settingsFragment"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="Sale Edits" />
        <item
            android:id="@+id/loginActivity"
            android:icon="@drawable/ic_menu_manage"
            android:title="Logout" />
    </group>

</menu>



<fragment
        android:id="@+id/navHostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"/>

1
为什么登录时不使用Fragment而要使用Activity? - Alex
抱歉回复晚了。 通过将登录作为片段,它会使工具栏出现在登录屏幕上,但我不想在登录屏幕上使用工具栏,因为这样为每个片段创建工具栏会增加额外的负担,而且它们都是相同的。 我对登录屏幕有一个完全不同的流程,例如登录->注册或登录->忘记密码等。我不想混淆它们。 - android_user
你可以使用登录片段,并从主活动中处理工具栏逻辑,通过 NavHostFragment.findNavController(nav_host_fragment)。addOnNavigatedListener() 方法来实现。这比登录活动更好的解决方案。 - Alex
你的意思是,在用户点击注销选项时,通过编程方式隐藏工具栏?并在登录后再次显示它? - android_user
是的,根据当前目标处理工具栏逻辑,我在这里添加了一些代码: https://dev59.com/8a3la4cB1Zd3GeqPFwGY#51491743 - Alex
3个回答

2

我认为更好的解决方案是使用登录作为片段而不是活动,并且应该从您的活动中处理工具栏。 您的活动应该类似于这样:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    supportActionBar?.setDisplayHomeAsUpEnabled(true)
    supportActionBar?.setDisplayShowHomeEnabled(true)
    setupActionBarWithNavController(this, findNavController(nav_host_fragment),drawer_layout)
    setupWithNavController(nav_view, findNavController(nav_host_fragment))

    NavHostFragment.findNavController( nav_host_fragment ).addOnNavigatedListener { controller, destination ->
        when(destination.id) {
            R.id.loginFragment -> {
                drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
                toolbar.visibility= View.GONE
            }
            R.id.dashboardFragment -> {
                drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
                toolbar.visibility= View.VISIBLE
            }
            else -> {
                drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
                toolbar.visibility= View.VISIBLE
            }
        }
    }
}

override fun onBackPressed() {
    if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
        drawer_layout.closeDrawer(GravityCompat.START)
    } else {
        super.onBackPressed()
    }
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when(item?.itemId) {
        android.R.id.home -> {
            if(NavHostFragment.findNavController( nav_host_fragment ).currentDestination.id==R.id.dashboardFragment) {
                drawer_layout.openDrawer(GravityCompat.START)
                return true
            }
        }
    }
    return super.onOptionsItemSelected(item)
}

override fun onSupportNavigateUp()= findNavController(nav_host_fragment).navigateUp()

我知道这可能是一种方法,但我最终在这里编写了太多的代码(不需要的),而且将不同的导航图合并成一个在未来很难维护。目前,我只是将登录和主屏幕的导航图分开,并在用户在登录屏幕上单击返回按钮时使用Intent返回到启动器。 - android_user

0

我也一直在尝试找到解决方案,最终发现它非常简单。

!!! 从 nav_graph.xml 中删除活动导航项以及所有处理或连接该项的代码实例。(例如,我使用AppBarConfiguration,必须从中删除id)

只需使用您的导航抽屉中要处理的菜单项的单击侦听器即可。

navView = findViewById(R.id.nav_view)
val logoutItem = navView.menu.findItem(R.id.loginActivity)
logoutItem.setOnMenuItemClickListener {
    val loginIntent = Intent(this, LoginActivity::class.java)
    startActivity(loginIntent)
    finish()
    return@setOnMenuItemClickListener true
}

0

回答你的问题:

我将LoginActivity添加到我的nav_graph中,并将其链接到抽屉中的注销菜单项,这是正确的方式吗?

是的,这就是启动新活动的方式,但您想要的与常规活动有些不同。

为了实现您想要的功能,您需要使用loginActivity启动应用程序(使其成为启动器活动),并且此活动将检查用户的会话(具有闪屏或加载屏幕),如果会话存在,则启动主要活动,否则用户必须登录,然后启动主要活动。

要使用导航组件执行此操作,您需要为登录活动创建一个新的导航图,并在其中链接您的主要活动。

通过这种设置,您应该在新任务中启动主要活动,然后完成登录活动,这样从主要活动返回也将关闭应用程序,从登录活动返回也将关闭应用程序。

如果这个答案不清楚,我可以解释得更详细。


我认为添加启动画面不是现代化的 Android 应用程序开发方式。此外,根据我在这里阅读的导航原则,不建议将登录屏幕设置为起始目标。https://developer.android.com/topic/libraries/architecture/navigation/navigation-principles - android_user
@android_user 这要看你的应用程序而定。启动画面是为了在检查用户会话时提供应用程序过渡的,但如果这是即时的并且不需要任何时间,则不需要启动画面。NavComponent仍在开发中,目前它的状态下,活动之间和嵌套图之间的导航还没有达到行业标准(至少我不能满足所有需求),因此您可以实现自定义导航器以适应您的需求,或者采用我的建议,直到有更好的方法。如果您发现其他解决方案,请告诉我。 - Kayvan N
我的应用程序设计不需要启动画面。我在应用程序启动时没有太多数据要加载,而且我的NavCompoment没有启动画面也能很好地工作。因此,目前除了当用户在登录屏幕上点击系统返回时,我只是使用Intent返回到启动器屏幕,这样我就能实现我想要的效果。未来可能会有更好的解决方案。 - android_user

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