安卓导航架构组件 - 导航抽屉图标

17

我目前正在使用Android Architecture Component的Navigation,但是我的Navigation Drawer出现了问题。在我的起始目的地时,它显示汉堡菜单,但其他Fragments显示向上箭头。我认为我的navigation_graph设置不正确。

在这里,您可以看到我的导航抽屉,显示了2个选项,主页和设置。当在任何一个Fragment中时,您应该看到汉堡图标。

nav drawer

然而,当导航到设置Fragment时,它显示向上箭头。

toolbar

navigation.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"
    app:startDestination="@id/nav_home">

    <!-- Start at HomeFragment -->
    <fragment
        android:id="@+id/nav_home"
        android:name=".HomeFragment"
        android:label="@string/home">

        <!-- Navigate to the Search -->
        <action
            android:id="@+id/action_nav_home_to_nav_search"
            app:destination="@id/nav_search" />
    </fragment>


    <fragment
        android:id="@+id/nav_settings"
        android:name=".SettingsFragment"
        android:label="@string/settings">

        <!-- Navigate to the Search -->
        <action
            android:id="@+id/action_nav_settings_to_nav_search"
            app:destination="@id/nav_search" />
    </fragment>



    <fragment
        android:id="@+id/nav_search"
        android:name=".SearchFragment"
        android:label="@string/search" />

</navigation>

我觉得HomeFragmentSettingsFragment应该有一定的相关性,但我不确定如何定义它。

main_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@id/nav_home"
            android:icon="@drawable/ic_home_white_24dp"
            android:title="@string/home" />

        <item
            android:id="@id/nav_settings"
            android:icon="@drawable/ic_settings_white_24dp"
            android:title="@string/settings" />
    </group>
</menu>

MainActivity

然后在 MainActivity 中,我就像这样设置它。我调用了 setupActionBarWithNavController,但我还必须自己设置导航抽屉,并处理 onNavigationItemSelected

private fun setupNavigation() {
    navController = findNavController(R.id.mainNavigationFragment)
    setupActionBarWithNavController(this, navController, drawer_layout)

    val toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        drawer_layout.addDrawerListener(toggle)
        toggle.syncState()

    nav_view.setNavigationItemSelectedListener(this)
}

override fun onNavigationItemSelected(item: MenuItem): Boolean {
    val current = navController.currentDestination.id
    if (item.itemId != current) {
        navController.navigate(item.itemId)
    }

    drawer_layout.closeDrawers()
    return true
}

构建.gradle

// Navigation
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha04'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha04'

谢谢。


只是确认一下,您想要汉堡图标和设置页面一起吗?还是其他的图标?但不是在设置页面中的向上按钮。 - Mohammad Tauqir
是的。两者都会显示汉堡图标,因为它们都是导航抽屉中的项目。然而,转到另一个屏幕,比如“搜索”或“详情”,会显示一个向上的图标。 - advice
使用两个 Activity 来分别实现主页和设置页面,创建一个基础 Activity 并从它扩展这两个 Activity。在基础 Activity 中调用初始化导航抽屉及其功能的方法。这应该能解决你的问题。 - Mohammad Tauqir
我正在使用导航库来减少活动的数量。我试图遵循谷歌的建议,即一个活动,多个片段。 - advice
另一种解决方案是在主页和设置页面的常规活动中使用FrameLayout作为容器,并通过编程方式替换片段。 - Mohammad Tauqir
让我们在聊天中继续这个讨论 - Mohammad Tauqir
7个回答

10

在新版本的alpha中(我使用的是1.0.0-alpha07),调用AppBarConfiguration构造函数时,他们增加了定义topLevelDestinationIds的可能性。

因此,我像这样设置我的NavController:

    val navController = findNavController(R.id.nav_host_fragment)
    val config = AppBarConfiguration(
        setOf(
            R.id.fistTopFragment,
            R.id.secondTopFragment,
            ...
        ),
        dr.drawerLayout
    )
    tb.setupWithNavController(navController, config)

其中dr是MaterialDrawer,tb是Toolbar。

然后它的行为更像Gmail,至少对于ActionBarDrawerToggle,返回栈仍然被保留。由于我必须自己处理MaterialDrawer中的项目选择,我将使用包括弹出到导航图的根片段的全局导航操作来减少返回栈操作,并且现在使用类似“欢迎屏幕”的东西。

另一种解决方法可能是自定义处理onBackPressed。首先,您必须从活动布局的主机片段中删除app:defaultNavHost="true"。像这样的东西

override fun onBackPressed() {
    val navController = findNavController(R.id.nav_host_fragment)
    if (navController.currentDestination == null
        || navController.currentDestination!!.id in setOf(
            R.id.fistTopFragment,
            R.id.secondTopFragment,
            ...
        )
    ) {
        super.onBackPressed()
    } else {
        navController.navigateUp()
    }
}

对于那些代码,很抱歉,我还在学习Kotlin,所以可能有更好的方法。


4
我很抱歉,它是导航组件的一个特性

当您在非根目标上时,操作栏还将显示“向上”按钮,并在根目标时显示抽屉图标,自动在它们之间进行动画处理。

如果您想要,可以尝试使用setupWithNavController(NavigationView,NavController)并自己处理工具栏。


我正在尝试遵循指南,在“根目标”上显示汉堡图标,在“非根目标”上显示向上按钮,但它在除“主页目标”以外的其他“根目标”上显示向上按钮。 - advice
1
根据创建者的意图,这是预期行为,所以你只是在与库进行斗争。对我来说,即使是对于抽屉,我实际上也喜欢有一个起始屏幕。当我进入设置时,很可能当我按返回键时,我想回到使用应用程序而不是结束在启动器中。 - ferini
实际上,其中一条评论说这实际上是一个 bug。并且正在被跟踪。https://issuetracker.google.com/issues/109675998 - advice
你所提到的问题涉及到嵌套图表,但这似乎不是你的情况。但无论如何,请尝试使用新版本。 - ferini
1
我意识到我实际上正在运行alpha04,而修复程序在alpha02中。我尝试将我的根目标作为嵌套图表,这更有意义,但仍然是相同的结果。它绝对不正确,因为转到应该显示UP图标的片段时,按预期不起作用,它会打开导航抽屉而不是向后移动。 - advice

2
我为这个问题做了一个简单的例子。解决方案与Almighty的答案几乎相同。https://github.com/isaul32/android-sunflower 首先创建一组顶级目的地。
val topLevelDestinations = setOf(R.id.garden_fragment,
        R.id.plant_list_fragment)
appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations)
        .setDrawerLayout(drawerLayout)
        .build()

然后像这样重写onSupportNavigateUp函数:

override fun onSupportNavigateUp(): Boolean {
    return NavigationUI.navigateUp(navController, appBarConfiguration)
}

1

如果您愿意,可以使用NavController.OnNavigatedListener并使用以下代码设置标题。

@Override
public void onNavigated(@NonNull NavController controller, @NonNull NavDestination destination) {
    mActivityBinding.toolbar.setTitle(destination.getLabel());
}

记得在导航图中设置标签。

1
BottomNavigationView相关的选项卡片段上出现返回箭头是一种预期行为。然而,即使在著名的应用程序(如Instagram、Youtube等具有底部选项卡的应用程序)中,也没有看到它被“原样”使用。例如,在Youtube应用程序中导航到不同的底部选项卡,然后单击设备的返回选项,您会注意到它返回到先前的选项卡片段,而不是退出应用程序。因此,bottomnavigationview选项卡片段在这里不是根目的地。
想要提醒您可能会遇到的其他重要问题: 然而,有几个钩子可以用于自定义行为:
  1. onBackPressed - to close drawer layout if open

    override fun onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
          drawerLayout.closeDrawer(GravityCompat.START)
        } else {
          super.onBackPressed()
        }
     }
    
  2. Add navController.addOnNavigatedListener(..) and inside the listener customise HomeAsUpIndicator icon

  3. Override onOptionsItemSelected to customise menu with id android.R.id.home action

  4. Set custom fragment navigator to customise how your fragments are treated (replace or show/ hide)

    navHostFragment = supportFragmentManager
            .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
    val customNavigator = CustomFragmentNavigator(navHostFragment.requireContext(),
            navHostFragment.childFragmentManager, navHostFragment.id)
    navHostFragment.navController.navigatorProvider.addNavigator(customNavigator)
    
    val inflater = navHostFragment.navController.navInflater
    val graph = inflater.inflate(R.navigation.main_nav_graph)
    navHostFragment.navController.graph = graph
    

请记住,导航架构组件仍处于alpha版本,因此请明智使用。


0

我认为你的菜单选项应该像这样:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
            android:id="@id/nav_home"
            android:icon="@drawable/ic_home_white_24dp"
            android:title="@string/home" />

        <item
            android:id="@id/nav_settings"
            android:icon="@drawable/ic_settings_white_24dp"
            android:title="@string/settings" />

</menu>

希望这可以帮到你。
祝编程愉快...

0

我完全同意这里的观点,但它是库的一部分。

setupActionBarWithNavController

为使用NavController设置由AppCompatActivity.getSupportActionBar()返回的ActionBar。

通过调用此方法,当目标更改时(假设存在有效标签),操作栏中的标题将自动更新。

您导航图的起始目的地被认为是唯一的顶级目的地。在您导航图的起始目的地上,如果给定的DrawerLayout非空,则ActionBar将显示抽屉图标。在所有其他目的地上,ActionBar将显示Up按钮。调用navigateUp(NavController,DrawerLayout)来处理Up按钮。

对我来说,这是有问题的,不是期望的结果,所以我仍然在使用它与NavigationView一起使用。

    navController = Navigation.findNavController(this, R.id.nav_host);
    NavigationUI.setupWithNavController(navigationView,navController);

然后添加一个监听器来更新标题和其他我想要更改的内容,就像这样

   navController.addOnNavigatedListener((controller, destination) -> {
        setToolbarColour(R.color.primary);
        switch (destination.getId()){
            case R.id.dashBoard :
                setToolbarColour(android.R.color.transparent);
                break;
            case R.id.requests :
                toolbar.setTitle(getString(R.string.request));
                break;

        }
    });

我为每个片段创建了一个新的导航图,虽然不是很好,但我得到了我想要的结果。


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