kotlin 导航组件设置标题崩溃

kotlin navigation components set title crash

我正在同时使用 navigation 组件和 bottomNavigationView。当我多次来回切换选项卡时,出现崩溃消息:

 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    at android.view.ViewGroup.addViewInner(ViewGroup.java:5361)
    at android.view.ViewGroup.addView(ViewGroup.java:5190)
    at android.view.ViewGroup.addView(ViewGroup.java:5162)
    at androidx.appcompat.widget.Toolbar.addSystemView(Toolbar.java:1528)
    at androidx.appcompat.widget.Toolbar.setTitle(Toolbar.java:777)
    at com.atp.newarchitecture.activity.AppActivity.onCreate$lambda-0(AppActivity.kt:119)

密码是:

navController.addOnDestinationChangedListener { _,
                                                        destination,
                                                        argument ->

    binding.toolbar.title = resources.getString(when (destination.id) {
            R.id.fragmentId -> R.string.fragmentTitle
            //... more ids
})

布局文件是:

<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:theme="@style/ToolBarTheme"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navigationIcon="?attr/homeAsUpIndicator" />

        <TextView
            android:id="@+id/selected_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@color/login_text_input_layout"
            app:layout_constraintBottom_toBottomOf="@+id/toolbar"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="1"
            tools:text="@string/selected_count" />

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/alert_tip_root"
            android:layout_width="0dp"
            android:layout_height="@dimen/quadruple_margin"
            android:layout_margin="@dimen/default_margin"
            android:visibility="gone"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:visibility="visible" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:background="?android:attr/windowBackground"
            android:theme="@style/BottomNavigationTheme"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/bottom_bar" />

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment_activity_main"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toTopOf="@id/bottom_navigation_view"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="1.0"
            app:navGraph="@navigation/nav_graph" />

        <View
            android:id="@+id/loading_background_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

我知道崩溃消息意味着一个视图应该只有 parent,但这里似乎没有 addView。想知道是否每次 binding.root.title 分配调用 addView?有什么帮助吗?谢谢!

编辑

def nav_version = "2.3.5"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")
    implementation("androidx.navigation:navigation-compose:2.4.0-beta02")

Navigation UI guide 中所述:

Caution: If you pass a Toolbar as the argument to setSupportActionBar(), the ActionBar assumes complete ownership of that Toolbar and you must not use any Toolbar APIs after that call.

在您调用 setSupportActionBar() 的所有情况下都是如此。这意味着直接调用任何 Toolbar API,例如 setTitle,将破坏该所有权契约并导致此类异常。

相反,您应该:

  1. 根本不要使用setSupportActionBar()。您可以 connect a Toolbar to Navigation directly and upgrade to AppCompat 1.4.0 让片段通过 MenuHostMenuProvider API 将元素添加到工具栏。

  2. 如果使用setSupportActionBar(),则需要通过ActionBar API设置标题。即,调用 supportActionBar!!.title = ...

  3. 按照 app bar guide 并在导航图 XML 文件中的每个目的地设置 android:label(即 android:label="@string/fragmentTitle")。这将在当前目的地更改时自动更新标题(假设您已调用 NavigationUI 的适当 setup 方法)。这将完全消除使用侦听器的需要,从而允许您完全删除所有代码。