Home > Software engineering >  kotlin navigation components set title crash
kotlin navigation components set title crash

Time:12-15

I'm using navigation components and bottomNavigationView together. When I switch the tabs back and forth many times, then I got crash message:

 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)

The code is:

navController.addOnDestinationChangedListener { _,
                                                        destination,
                                                        argument ->

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

the layout file is:

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

I know the crash message means one view should only has a parent, but seems no addView here. Wondering if every time binding.root.title assign call addView? Any help? Thanks!

EDIT

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

CodePudding user response:

As explained in the 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.

This is true in every case where you call setSupportActionBar(). This means that directly calling any Toolbar API, such as setTitle, is going to break that ownership contract and lead to these kind of exceptions.

Instead, you should either:

  1. Don't use setSupportActionBar() at all. You can connect a Toolbar to Navigation directly and upgrade to AppCompat 1.4.0 to have fragments add elements to the Toolbar via the MenuHost and MenuProvider APIs.

  2. If you use setSupportActionBar(), then you need to set the title via the ActionBar APIs. Namely, calling supportActionBar!!.title = ...

  3. Follow the app bar guide and set an android:label on each destination in your navigation graph XML file (i.e., android:label="@string/fragmentTitle"). This will automatically update the title as the current destination changes (assuming you've called the appropriate setup method of NavigationUI). This would remove the need to use your listener at all, thus allowing you to delete all of your code entirely.

  • Related