1

I am simply trying to add a badge to display a number in a small circle over my floating action button. I get this error:

java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant). at com.google.android.material.internal.ThemeEnforcement.checkTheme(ThemeEnforcement.java:241) at com.google.android.material.internal.ThemeEnforcement.checkMaterialTheme(ThemeEnforcement.java:215) at com.google.android.material.badge.BadgeDrawable.(BadgeDrawable.java:464) at com.google.android.material.badge.BadgeDrawable.createFromAttributes(BadgeDrawable.java:353) at com.google.android.material.badge.BadgeDrawable.create(BadgeDrawable.java:321) ...

when I do this:

val badge = BadgeDrawable.create(applicationContext)
BadgeUtils.attachBadgeDrawable(badge, binding.fab, null)

In my theme.xml file I have:

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Theme.Made" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/red</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/black</item>
        <item name="colorOnSecondary">@color/white</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">@color/black</item>
        <!-- Customize your theme here. -->
    </style>

    <style name="Theme.Made.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="Theme.Made.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="Theme.Made.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>

I have also a file themes.xml (night) but it is empty:

<resources xmlns:tools="http://schemas.android.com/tools"></resources>

In my AndroidManifest.xml I have:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"             xmlns:tools="http://schemas.android.com/tools">
    <application
        ...
        android:theme="@style/Theme.Made"
        ...
    >
    ...
    </application>
</manifest>

this is the layout of the main activity (where the floating action button is):

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:background="@color/white"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.Made.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/Theme.Made.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="@dimen/standard_margin"
        android:layout_marginBottom="16dp"
        app:srcCompat="@drawable/cart"
        android:contentDescription="@string/cart" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

the bindings are inflated by

binding = ActivityMainBinding.inflate(layoutInflater)

in the onCreate of the main activity.

What am I missing? why I get that error also if the theme is MaterialComponents? I have tried to change the theme to Theme.MaterialComponents.Light.DarkActionBar, Theme.MaterialComponents.Light.Bridge, ... like suggested in other questions, but the error is still presente. Thank you.

NB: i have already tried all what have been suggested in this link: similar question, indeed, from what I can see from my code, I am applying a MaterialComponents theme

  • [Related](https://stackoverflow.com/q/53476115/6576302). – C.F.G Dec 27 '22 at 14:20
  • Does this answer your question? [Error : IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents](https://stackoverflow.com/questions/53476115/error-illegalargumentexception-the-style-on-this-component-requires-your-app) – C.F.G Dec 27 '22 at 16:32
  • @C.F.G, unfortunately not, as you can read i tried to change my theme to those suggested in that answer and it did not work – Massimiliano Carosi Dec 27 '22 at 16:34
  • Sorry :( ... Try [this one!](https://stackoverflow.com/a/34568634/6576302) – C.F.G Dec 27 '22 at 17:39
  • @C.F.G i tried but got the same error, can the point be that the theme of the floating action button is not that of the main activity for some reason? do I have to specify a theme specifically for the fab, but how in this case? – Massimiliano Carosi Dec 27 '22 at 18:22
  • 2
    `BadgeDrawable.create(applicationContext)` – Don't use `applicationContext` there. Use the current `Activity` instead; e.g., `BadgeDrawable.create(this@MainActivity)`. Despite the `android:theme` on the `` element, the application `Context` doesn't actually have a theme set, by default. That attribute just sets the default theme for the Activities. – Mike M. Dec 30 '22 at 22:52
  • 1
    Besides Mike M hint, remove the night version of the `themes.xml` or at least add a `MaterialComponents` descendant as the default style; probably the default on your testing device is the night mode – Zain Dec 30 '22 at 23:03

1 Answers1

1

Update your code like this

binding.fab.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        binding.fab.viewTreeObserver.removeOnGlobalLayoutListener(this)
        val badgeDrawable = BadgeDrawable.create(this@MainActivity)
        badgeDrawable.number = 3
        badgeDrawable.isVisible = true
        BadgeUtils.attachBadgeDrawable(badgeDrawable, binding.fab, null)
    }
})

Or

binding.fabBadge.doOnPreDraw {
    val badgeDrawable = BadgeDrawable.create(this@Question12_Badge)
    badgeDrawable.number = 3
    badgeDrawable.isVisible = true
    BadgeUtils.attachBadgeDrawable(badgeDrawable, binding.fabBadge, null)
}
Sohaib Ahmed
  • 1,990
  • 1
  • 5
  • 23
  • Thank you, it worked. I am not removing the listener, because the visibility of the floating action button changes in run time and the badges can be added when it become visible. – Massimiliano Carosi Jan 03 '23 at 10:38
  • You are welcome. I'm happy it worked for you. Can you accept it too? So, others may find this helping. – Sohaib Ahmed Jan 03 '23 at 10:49
  • I already accepted. Please can you give me some more information. I need to have the listener live because of the reasons exposed above. Still, if navigate and get back I have multiple call to the listener, also if I remove it in onDestroyView. Am i missing anything? – Massimiliano Carosi Jan 03 '23 at 11:24
  • Actually, you don't need to remove listener in `onDestroyView`. just copy/paste my code in `onViewCreated`. for the next time,,, just call the inner 4 lines, it will update val badgeDrawable = BadgeDrawable.create(this@MainActivity) badgeDrawable.number = 3 badgeDrawable.isVisible = true BadgeUtils.attachBadgeDrawable(badgeDrawable, binding.fab, null) – Sohaib Ahmed Jan 03 '23 at 11:36
  • it's not enough, i toggle the badge and visibility of the object, anytime i add a badge after a set it visible i have the problem, your code automatically removes the listener, so it work when o create the view but not during the life of it, i solved by letting the listener on (deleting the line binding.fab.viewTreeObserver.removeOnGlobalLayoutListener(this)) but when i navigate away and back i get more calls anytime the layout changes – Massimiliano Carosi Jan 03 '23 at 11:44
  • 1
    It is a global listener, it will listen anytype of change to this view. you may try for `doOnPreDraw`, Updated my answer too\ – Sohaib Ahmed Jan 03 '23 at 11:51
  • of course, it is needed only once, my fault, thank you again – Massimiliano Carosi Jan 03 '23 at 12:12