0

I was working on adding a top bar to my bottom navigation view selected item, I followed this thread. But I got hit with a cast exception and the app crash. How to fetch the BottomNavigationItemView and add view to it so that I can display the top bar.

 FATAL EXCEPTION: main
    Process: com.abc.project, PID: 26285
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.abc.project/com.abc.project.views.HomeActivity}: java.lang.ClassCastException: com.google.android.material.bottomnavigation.BottomNavigationView cannot be cast to com.google.android.material.bottomnavigation.BottomNavigationItemView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3482)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3634)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2094)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7815)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1094)
    Caused by: java.lang.ClassCastException: com.google.android.material.bottomnavigation.BottomNavigationView cannot be cast to com.google.android.material.bottomnavigation.BottomNavigationItemView
        at com.abc.project.views.BottomNavigationHelper.showBadge(HomeActivity.kt:128)
        at com.abc.project.views.HomeActivity.onCreate(HomeActivity.kt:77)
        at android.app.Activity.performCreate(Activity.java:8004)
        at android.app.Activity.performCreate(Activity.java:7988)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3455)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3634) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2094) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:223) 
        at android.app.ActivityThread.main(ActivityThread.java:7815) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1094) 

onCreate of the activity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        databinding=DataBindingUtil.setContentView(this,R.layout.activity_home)

        viewmodel=ViewModelProvider(this,factory).get(HomeViewmodel::class.java)

        BottomNavigationHelper().showBadge(this, databinding.bottomNavView, R.id.bottom_nav_view)
    }
    

HelperClass for interacting with BottomNavigationView

class BottomNavigationHelper {

    fun showBadge(context: Context, bottomNavigationView: BottomNavigationView, @IdRes itemId: Int) {
        removeBadge(bottomNavigationView, itemId)
        val itemView = bottomNavigationView.findViewById<BottomNavigationItemView>(itemId)
        val badge = LayoutInflater.from(context).inflate(R.layout.layout_bottom_nav_badge, bottomNavigationView, false)
        itemView.addView(badge)
    }

    fun removeBadge(bottomNavigationView: BottomNavigationView, @IdRes itemId: Int) {
        val itemView = bottomNavigationView.findViewById<BottomNavigationItemView>(itemId)
        if (itemView.childCount == 4) {
            itemView.removeViewAt(2)
        }
    }
}

xml code for the activity

<?xml version="1.0" encoding="utf-8"?>
<layout
    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">
    <data>
        <variable
            name="data"
            type="com.abc.project.viewmodels.HomeViewmodel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/cream"
        tools:context=".views.HomeActivity">

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/bottom_nav_view"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_nav_view"
            style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:itemBackground="@color/cream"
            app:itemIconTint="@color/bnv_tab_item_foreground"
            app:itemTextColor="@color/bnv_tab_item_foreground"
            app:labelVisibilityMode="labeled"
            android:paddingVertical="4sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:menu="@menu/menu_bottom_nav" />

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


Stuck with this issue. Any help will be appreciated.

Vishnu Satheesh
  • 541
  • 1
  • 8
  • 11

1 Answers1

1

This is where you're casting to BottomNavigationItemView:

fun showBadge(
    context: Context,
    bottomNavigationView: BottomNavigationView,
    @IdRes itemId: Int) {
    ...
    val itemView = bottomNavigationView.findViewById<BottomNavigationItemView>(itemId)

By providing that type in the findViewById call, you're specifying that whatever view is found with itemId will be a BottomNavigationItemView. But here's where you're calling that function:

BottomNavigationHelper().showBadge(this, databinding.bottomNavView, R.id.bottom_nav_view)

The ID you're passing is bottom_nav_view, and the View in your XML with that ID is a BottomNavigationView, not a BottomNavigationItemView. And that's what your error is explaining:

Caused by: java.lang.ClassCastException: 
    com.google.android.material.bottomnavigation.BottomNavigationView cannot be cast
    to com.google.android.material.bottomnavigation.BottomNavigationItemView
        at com.abc.project.views.BottomNavigationHelper.showBadge(HomeActivity.kt:128)

As far as fixing it goes, I don't know what a BottomNavigationItemView is supposed to be, it's not part of the Material library. You could ask the person who posted the answer you're working from by adding a comment?

The approach they're going for is basically

  • add items to the navigation bar with unique IDs
  • inside the BottomNavigationView (which is a FrameLayout), find the View with a particular ID
  • add your badge View to that found View (which implies that what you found is actually a ViewGroup since that's what has an addView method)

Offhand I don't know how BottomNavigationViews work internally, if the items they create from your menu actually are ViewGroups, etc. So I don't know if this approach will actually work, I'm just giving you a line of investigation really.


That said, the Material Design library allows you to add badges to your bottom navigation items natively (scroll down to Adding badges). This lets you add a BadgeDrawable which may or may not be customisable to what you want. I'd look into the stuff like that which is provided, before you start trying to manipulate the view hierarchy directly

cactustictacs
  • 17,935
  • 2
  • 14
  • 25
  • Appreciate the effort you took to answer my query. I Implemented the same using a GitHub library. Thank you. https://github.com/Droppers/AnimatedBottomBar – Vishnu Satheesh May 09 '23 at 11:32