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

Posted

技术标签:

【中文标题】带有 Jetpack Navigation 的 BottomNavigationView 未正确显示活动菜单指示器【英文标题】:BottomNavigationView with Jetpack Navigation not correctly showing the active menu indicator 【发布时间】:2021-12-22 07:44:34 【问题描述】:

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

假设我有 4 个导航菜单 BottomNavigationView,片段 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 是正确的。但是,活动菜单指示器仍位于第 2 个菜单而不是第 1 个菜单。我希望在第一个菜单中出现活动菜单指示器,因为我从片段 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)

感谢您的帮助!

【问题讨论】:

【参考方案1】:

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

你想让导航像:

A (A highlighted) -&gt; A1 -&gt; B (A highlighted) -&gt; Back pressed -&gt; A1 (A highlighted)

但是你得到了:

A (A highlighted) -&gt; A1 -&gt; B (A highlighted) -&gt; Back pressed -&gt; A1 (B highlighted)

所以,当您从 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 片段分开。

更新:

我希望当按下返回时,它仍然显示片段 A1,并且指示器位于第一个菜单。相反,您的解决方案将我导航到片段 A。

为此,您通常只弹出一次返回堆栈,并设置要检查的BottomNavView 菜单项:

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

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

在活动或父片段:

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() 
    
)

【讨论】:

抱歉迟到了,我认为解决方案不符合我的期望。我想按下回时,它仍然显示片段 A1 并且指示器位于第一个菜单。相反,您的解决方案将我导航到片段 A。 对不起,如果您误解了;让我再想想 @BernhardJosephus 请查看更新后的答案UPDATE 部分。 对不起,我刚刚有时间检查它,它有效。非常感谢你的帮助! ^^

以上是关于带有 Jetpack Navigation 的 BottomNavigationView 未正确显示活动菜单指示器的主要内容,如果未能解决你的问题,请参考以下文章

JetPack架构---Navigation的使用

Jetpack系列 — Navigation

jetpack之navigation

jetpack之navigation

jetpack之navigation

jetpack之navigation