Android 导航组件:BottomNavigationView 的选定选项卡图标未更新

Ami*_*mir 18 navigation android bottomnavigationview android-safe-args

我正在BottomNavigationView与导航组件一起使用。当显示片段不是根片段时,选项卡图标不会更新(选择)。

示例:
当我在带有片段 A(也是根片段)的Tab Home和带有片段 B(也是根片段)的Tab Star之间切换时,它工作正常。
但是,当我从Tab Home导航到另一个片段(例如片段 A2),然后点击Tab Star并再次返回Tab Home时,仍然在 中选择Tab StarBottomNavigationView

Android 导航组件错误

版本运行良好2.4.0-alpha05,当我将其更新到2.5.0-alpha01.

构建.gradle(应用程序)

implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-dynamic-features-fragment:2.5.0-alpha01"
Run Code Online (Sandbox Code Playgroud)

构建.gradle(根)

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-alpha01"
Run Code Online (Sandbox Code Playgroud)

图形:
Android 导航图

<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/graph"
        app:startDestination="@id/fragmentA">
    <fragment
            android:id="@+id/fragmentA"
            android:name="ui.test.FragmentA"
            tools:layout="@layout/fragment_test"
            android:label="FragmentA" >
        <action
                android:id="@+id/action_fragmentA_to_fragmentA2"
                app:destination="@id/fragmentA2" />
    </fragment>
    <fragment
            android:id="@+id/fragmentA2"
            android:name="ui.test.FragmentA2"
            tools:layout="@layout/fragment_test"
            android:label="FragmentA2" />
    <fragment
            android:id="@+id/fragmentB"
            android:name="ui.test.FragmentB"
            tools:layout="@layout/fragment_test"
            android:label="FragmentB" />
</navigation>
Run Code Online (Sandbox Code Playgroud)

菜单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/fragmentA"
            android:icon="@drawable/ic_home"
            android:title="" />
    <item
            android:id="@+id/fragmentB"
            android:icon="@drawable/ic_star"
            android:title="" />
</menu>
Run Code Online (Sandbox Code Playgroud)

难道我做错了什么?或者这是错误?
我该如何解决这个问题?

hdo*_*ort 24

所以对我有用的是 ianhanniballake 在他的回答中暗示的解决方案:使用 setOnItemSelectedListener。

// always show selected Bottom Navigation item as selected (return true)
bottomNavigationView.setOnItemSelectedListener { item ->
  // In order to get the expected behavior, you have to call default Navigation method manually
  NavigationUI.onNavDestinationSelected(item, navController)

  return@setOnItemSelectedListener true
}
Run Code Online (Sandbox Code Playgroud)

这将始终选择该项目并导航到关联的目的地,同时维护多个返回堆栈。


ian*_*ake 8

鉴于您的导航图,无法fragmentA2与您的菜单项关联fragmentA,因此fragmentA当您返回 时不会选择fragmentA2。根据这个问题

NavigationUI始终使用当前目的地以及它所属的图表作为应选择哪个选项卡的事实来源。

navigate()这可以通过调用“转到”来看到SecondFragment- 即使您没有使用底部导航按钮,所选选项卡也已更改,因为当前目的地已更改为R.id.frag_second

因此,当您navigate()通过R.id.frag_hint中的按钮时HomeFragmentNavigationUI会收到当前目的地已更改为 的回调R.id.frag_hint。它查看NavDestination并注意到没有匹配的菜单项R.id.frag_hint。然后它会查看目的地的父图 - 您的R.id.sample <navigation>元素。也没有与该 ID 匹配的菜单项,因此NavigationUI无法将该目的地与任何菜单项相关联,因此什么也不做。所有版本的导航都是如此。

那么当您点击底部导航项时有什么不同呢?好吧,NavigationUI事实上从一个角度来看没有什么不同:运行完全相同的代码,当前目的地以及它所属的图表是应该选择哪个选项卡的事实来源。在导航 2.3.5 中,没有为每个选项卡保存状态,因此它只能“起作用”,因为选择选项卡会强制当前目的地的 ID 与您刚刚点击的菜单项的目的地相匹配。

R.id.frag_hint因此,您在示例应用程序中看到的是,任何菜单项之间都没有链接,这意味着NavigationUI不执行任何操作。如果您想链接R.id.frag_hint到“主页”选项卡,那么这正是嵌套导航图的用途。

即,您的导航图应该如下所示:

<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/sample"
    app:startDestination="@id/home">
   
    <navigation
        android:id="@+id/home"
        app:startDestination="@id/frag_home">
        <fragment
            android:id="@+id/frag_home"
            android:name="eu.rekisoft.android.navbug.HomeFragment"
            tools:layout="@layout/fragment_home">
            <action
                android:id="@+id/cause_bug"
                app:destination="@id/frag_hint"/>
        </fragment>

        <fragment
            android:id="@+id/frag_hint"
            android:name="eu.rekisoft.android.navbug.HintFragment"
            android:label="Hint"
            tools:layout="@layout/fragment_hint"/>
    </navigation>

    <fragment
        android:id="@+id/frag_second"
        android:name="eu.rekisoft.android.navbug.SecondFragment"
        android:label="Second Fragment"
        tools:layout="@layout/fragment_second"/>

</navigation>
Run Code Online (Sandbox Code Playgroud)

并且您的菜单 XML 应该更新以android:id="@id/home"匹配您的导航图。

现在,当您选择主页底部导航项时,当前目的地将更改为R.id.frag_hint(由于导航 2.4 对多个后退堆栈的支持,您的状态已恢复)并NavigationUI查看 ID -R.id.frag_hint仍然不匹配任何菜单项,但是现在父图的 IDR.id.home 确实与菜单项(您的主菜单项)匹配,因此它被选中。

导航图及其结构驱动 UI 的意图是如何NavigationUI工作并按预期工作的关键部分(Navigation 2.4 的早期版本中有一个错误打破了这一驱动原理,但此后已在 beta02 中修复) 。所有这些NavigationUI都是专门基于公共 API 构建的,因此如果您想使用一些不同的逻辑来选择独立于导航图结构的底部导航项,您绝对可以这样做。

您将从源代码onNavDestinationSelected中注意到,您可以使用您的public 调用MenuItem来获得完全相同的navigate()逻辑,该逻辑保留您自己从 返回任何值的能力setOnItemSelectedListener(这是控制选项卡是否被选中的原因)。同样,您自己OnDestinationChangedListener可以选择查看hierarchy目的地的 来选择是否更改所选的底部导航项。

因此,您的图表还应该为每个选项卡使用嵌套图表:

<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/graph"
        app:startDestination="@id/graphA">
    <navigation
        android:id="@+id/graphA"
        app:startDestination="@id/fragmentA">
        <fragment
            android:id="@+id/fragmentA"
            android:name="ui.test.FragmentA"
            tools:layout="@layout/fragment_test"
            android:label="FragmentA" >
            <action
                android:id="@+id/action_fragmentA_to_fragmentA2"
                app:destination="@id/fragmentA2" />
        </fragment>
        <fragment
            android:id="@+id/fragmentA2"
            android:name="ui.test.FragmentA2"
            tools:layout="@layout/fragment_test"
            android:label="FragmentA2" />
    </navigation>
    <navigation
        android:id="@+id/graphB"
        app:startDestination="@id/fragmentB">
        <fragment
            android:id="@+id/fragmentB"
            android:name="ui.test.FragmentB"
            tools:layout="@layout/fragment_test"
            android:label="FragmentB" />
    </navigation>
</navigation>
Run Code Online (Sandbox Code Playgroud)

菜单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/graphA"
            android:icon="@drawable/ic_home"
            android:title="" />
    <item
            android:id="@+id/graphB"
            android:icon="@drawable/ic_star"
            android:title="" />
</menu>
Run Code Online (Sandbox Code Playgroud)

  • 那么这意味着任何全局使用的片段都是不可能的?同样,当从两个不同的菜单项使用相同的目的地(如详细信息视图)时,这也不会像用户期望的那样工作,对吧?那么我必须为两个菜单项复制该详细视图吗?难道这不应该通过检查运行时堆栈而不是导航图来更好地完成吗? (5认同)