带有 Jetpack Navigation 的 BottomNavigationView 无法正确显示活动菜单指示器

BottomNavigationView with Jetpack Navigation not correctly showing the active menu indicator

我正在尝试使用 Jetpack Navigation 解决 BottomNavigationView 的一些导航问题。我正在使用 Jetpack Navigation 2.4.0-beta02。第一个问题是后退按钮总是让我回到起始目的地。这个加menuCategory: secondary就解决了。但是,还有一个问题。

假设我有 BottomNavigationView 的 4 个导航菜单,片段 A、B、C 和 D。然后,还有另一个片段 A1,它不属于 BottomNavigationView。导航图如下所示:

<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/mobile_navigation"
    app:startDestination="@+id/navigation_a">

    <fragment
        android:id="@+id/navigation_a"
        android:name="com.example.FragmentA"
        android:label=""
        tools:layout="@layout/fragment_a">
        <action
            android:id="@+id/action_navigation_a_to_navigation_a1"
            app:destination="@id/navigation_a1"
            app:launchSingleTop="true" />
    </fragment>
    <fragment
        android:id="@+id/navigation_a1"
        android:name="com.example.FragmentA1"
        android:label=""
        tools:layout="@layout/fragment_a1" />
    <fragment
        android:id="@+id/navigation_b"
        android:name="com.example.FragmentB"
        android:label=""
        tools:layout="@layout/fragment_b" />
    <fragment
        android:id="@+id/navigation_c"
        android:name="com.example.FragmentC"
        android:label=""
        tools:layout="@layout/fragment_c" />
    <fragment
        android:id="@+id/navigation_d"
        android:name="com.example.FragmentD"
        android:label=""
        tools:layout="@layout/fragment_d" />
</navigation>

因此,如果我从片段 A 导航到 A1,然后导航到片段 B(现在活动菜单指示器位于第二个菜单),然后我按下硬件后退按钮,它会显示正确的片段 A1。但是,活动菜单指示器仍然在第二个菜单而不是第一个菜单。我希望在第一个菜单中出现活动菜单指示器,因为我从片段 A 导航到片段 A1。

这是我的 menu.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_a"
        android:title="A"
        android:menuCategory="secondary" />

    <item
        android:title="B"
        android:id="@+id/navigation_b"
        android:menuCategory="secondary" />

    <item
        android:title="C"
        android:id="@+id/navigation_c"
        android:menuCategory="secondary" />

    <item
        android:title="D"
        android:id="@+id/navigation_d"
        android:menuCategory="secondary" />
</menu>

MainActivity.kt

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_main) as NavHostFragment
navController = navHostFragment.navController

binding.navView.setupWithNavController(navController)

感谢您的帮助!

现在 BottomNavigationView 有 4 个片段:A、B、C 和 D。

并且您想将导航设置为:

A (A highlighted) -> A1 -> B (A highlighted) -> Back pressed -> A1 (A highlighted)

但是你得到了:

A (A highlighted) -> A1 -> B (A highlighted) -> Back pressed -> A1 (B highlighted)

因此,当您 return 从 B 回来时,您需要突出显示 A。

要做到这一点,您需要在片段 B 处按下后退时检查后退堆栈中的前一个片段。

伪代码:

// At fragment B
if (back_pressed) {

    if (previous_fragment is A1) {
        popup the back stack twice to return back to A
    } else {
        popup the back stack once (Normal behavior of the back stack)
    }

}

代码:

class FragmentB : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_b, container, false)

        requireActivity().onBackPressedDispatcher.addCallback(
            viewLifecycleOwner,
            object : OnBackPressedCallback(true) {
                override
                fun handleOnBackPressed() { // Back pressed
                    
                    val previousFragment = findNavController().previousBackStackEntry?.destination?.id
                    previousFragment?.let {
                        when (previousFragment) {
                            R.id.navigation_a1 ->
                                findNavController().navigateUp()
                            else ->
                                Log.d(TAG, "onCreateView: Not A1")
                        }
                        findNavController().navigateUp() // Normal behaviour when back is pressed
                    }
                                    
                }
            })

        return view

    }
}

旁注:

这适用于当前的 navGraph 设置;但我鼓励你有另一个图表,甚至是当前图表中的嵌套导航;这样您就可以将片段 A1 与 BottomNavView 个片段分开。

更新:

I want when pressing back, it still shows fragment A1 and the indicator is at the 1st menu. Your solution instead navigates me to fragment A.

为此,您通常只弹出返回堆栈一次,并将 BottomNavView 菜单项设置为选中:

假设 BottomNavView 仅由 activity 托管,因此您可以使用 requireActivity() as MainActivity 访问它;将其转换为您的 activity.

的名称

或者,如果它由片段托管,则使用 parentFragment as MyParentFragment,类似地将其转换为父片段的名称。

在 activity 或父片段:

fun highlightAItem() {
    // Highlight A item from the bottomNavView
    val item = navView.menu.findItem(R.id.navigation_a)
    item.isChecked = true
}

在片段 B:

requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
    override
    fun handleOnBackPressed() {
        // Highlight A item from the BottomNavView
        (requireActivity() as MainActivity).highlightAItem() 
        // Pop backstack once to return to A1 fragment
        findNavController().navigateUp() 
    }
})