26

I want to implement notification badge with android.support.design.widget.TabLayout. I had tried my best effort to implement it but fails.

Any help would by greatly appreciated.

perror
  • 7,071
  • 16
  • 58
  • 85
Arjun sharma
  • 303
  • 1
  • 3
  • 8

12 Answers12

39

EDIT: UPDATE

With the newest release of the Material Components, the Android team now offers an official BadgeDrawable to be used on the TabLayout to achieve the asked result. It is part of the v1.1.0-alpha07 update. https://github.com/material-components/material-components-android/releases/tag/1.1.0-alpha07

The usage is very simple and can be done as following:

tabLayout.getTabAt(0).showBadge().setNumber(1);

See the integration in the material components demo app: https://github.com/material-components/material-components-android/commit/111cd001f5302bd6899f181ab7ccea2fd4002f63#diff-bf3e9a8a208f0ecab05088823fba99c4R239


OLD ANSWER

I tried some of the above mentioned solutions, but they did not deliver proper results. If you take a closer look in the TabLayout implementation you will realize that the TabView will try to get the textView and iconView from the CustomView if applied.

So instead of doing some hacky implementations I rather came up with a layout equal to the one from the original TabView but an additional view which can have a badge.

So you will need the badged_tab.xml layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_marginTop="4dp">

        <ImageView
            android:id="@android:id/icon"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:scaleType="centerInside" />

        <TextView
            android:id="@android:id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:gravity="center"
            android:maxLines="2" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/badgeCotainer"
        android:layout_width="wrap_content"
        android:layout_height="16dp"
        android:layout_marginStart="12dp"
        android:background="@drawable/notifications_background"
        android:gravity="center"
        android:minWidth="16dp"
        android:visibility="gone">

        <TextView
            android:id="@+id/badge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textColor="#fff"
            android:textSize="10sp" />
    </LinearLayout>
</RelativeLayout>

And some sort of notification background:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/red" />
</shape>

When adding your tab programatically just call

tab.setCustomView(R.layout.badged_tab);

And then you can show / hide / set a badge cound at any time via:

if(tab != null && tab.getCustomView() != null) {
    TextView b = (TextView) tab.getCustomView().findViewById(R.id.badge);
    if(b != null) {
        b.setText(notifications + "");
    }
    View v = tab.getCustomView().findViewById(R.id.badgeCotainer);
    if(v != null) {
        v.setVisibility(View.VISIBLE);
    }
}
mikepenz
  • 12,708
  • 14
  • 77
  • 117
  • 1
    I used this approach, and it worked for me. I adapted this solution to add a custom view that contained the badge, and then simply setCustomView(null) to remove it when it wasn't needed in the UI. Creating a custom view that has the same spacings and margins is important so you get a seamless transition to the default view for the respective tab. I used this successfully with TabLayouts. Alternatively, you can use a custom view on all tabs and switch between badge and non-badged layouts as required if you're having trouble with UI consistency between them. – angryITguy May 09 '17 at 00:49
  • Best solution of the problem so far. – flash Jul 04 '18 at 11:47
  • This worked for me, but for any others finding this post, can you add android:orientation="vertical" to the first Linear Layout in badged_tab.xml? I believe that is the desired result. – Robert Bentley May 31 '19 at 19:41
  • Please note that there is now a better way via the latest material components update – mikepenz Jun 03 '19 at 08:44
  • but this material design shows badge counter on my TabTitle ,,it looks creeep...Is there any way I can give orientation or gravity to badge counter? – Android Geek Jul 12 '19 at 05:26
  • For newer version for material TabLayout you can use getOrCreateBadge() tabLayout.getTabAt(0).getOrCreateBadge() .setNumber(1); – Cjames Nov 07 '19 at 05:00
  • How to use the updated answer? I'm unable to access BadgeDrawable. What import library should I put in my gradle? – luhluh Dec 02 '19 at 00:12
  • did showBadge() get removed? It's also no longer available for bottomnavigationview, but getOrCreateBadge it there – behelit Jul 01 '20 at 03:14
  • Getting @id/text is private when using the @android:id notation – user1114 Apr 26 '21 at 16:27
  • 'private' error was because I changed text1 to text – user1114 Apr 26 '21 at 16:44
16

I would suggest you look at this website:

https://guides.codepath.com/android/Google-Play-Style-Tabs-using-TabLayout#design-support-library

You can iterate through the different tabs using this method and set the custom views to whatever you want:

  // Iterate over all tabs and set the custom view
    for (int i = 0; i < tabLayout.getTabCount(); i++) {
        TabLayout.Tab tab = tabLayout.getTabAt(i);
        tab.setCustomView(pagerAdapter.getTabView(i));
    }

public View getTabView(int position) {
    // Given you have a custom layout in `res/layout/custom_tab.xml` with a TextView and ImageView
    View v = LayoutInflater.from(context).inflate(R.layout.custom_tab, null);
    TextView tv = (TextView) v.findViewById(R.id.textView);
    tv.setText(tabTitles[position]);
    ImageView img = (ImageView) v.findViewById(R.id.imgView);
    img.setImageResource(imageResId[position]);
    return v;
}

}
Anirudh Sharma
  • 7,968
  • 13
  • 40
  • 42
Simon
  • 19,658
  • 27
  • 149
  • 217
  • 1
    i tried this and its working, but if for example i selected the custom tab and want the tab to return to its default view after that (having normal and selected color) like the other tabs, how can i do that – Rashad.Z Oct 02 '15 at 13:42
  • This should be the accepted answer. Works perfectly fine with great explanation in the link. – Shubhral Mar 14 '17 at 14:00
  • it's working but how to set default width and height like other tabs? – vishal patel May 25 '17 at 09:28
7

My solution to this was using https://github.com/jgilfelt/android-viewbadger and setting a custom view to each tab:

My tabs have only icons so I used ImageView, but I believe you can use any other view, check https://github.com/jgilfelt/android-viewbadger/blob/master/README.markdown:

private BadgeView badge;

Tab tab = tabLayout.getTabAt(position);
ImageView imageView = new ImageView(context);
tab.setCustomView(imageView);
badge = new BadgeView(context, imageView);
lucas_sales
  • 129
  • 2
5

This is an old cuestion, but Its much more simple these days. Im using Kotlin, AndroidX, using the next widget as TabLayout:

com.google.android.material.tabs.TabLayout

and inside my gradel app file:

implementation 'com.google.android.material:material:1.1.0-alpha09'

For setting a simple badge, just write..

yourTabLayout?.getTabAt(currentTabPosition)?.apply{
                        orCreateBadge
                        badge?.isVisible = true
                    }

Then just set isVisible = false to hide, like this:

private fun changeYourTabMethod(newTabPosition : Int) {
    // do some stuff and then hide the badge...
    yourTabLayout?.getTabAt(newTabPosition)?.apply{
        badge?.isVisible = false
    }
}
markomoreno
  • 318
  • 3
  • 5
5

I will use this:

  tabLayout.getTabAt(0).getOrCreateBadge().setNumber(10);
Javier Monzon
  • 59
  • 1
  • 1
3

I don' know why but none of the above answers worked for me :(

I have my own Solution that is gonna make the same tablayout as the whatsapp with that badge :)

First make a custom tab Layout as custom_tab

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:padding="12dp">

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Calls"
        android:textColor="@drawable/tab_text_color_selector"
        android:textSize="@dimen/large_text" />

    <TextView
        android:id="@+id/tv_count"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_marginLeft="6dp"
        android:background="@drawable/badge_background"
        android:gravity="center"
        android:text="99"
        android:textColor="@color/colorPrimary"
        android:textSize="@dimen/medium_text" />

</LinearLayout>

</RelativeLayout>

Second a badge_background

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item xmlns:android="http://schemas.android.com/apk/res/android">
    <shape android:shape="oval">
        <solid android:color="@drawable/tab_text_color_selector" />
    </shape>
</item>
</layer-list>

Third a tab_color_selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:color="@color/colorTextPrimary"android:state_selected="true" />
<item android:color="@color/colorAccent"/>
 </selector>

Fourth in your activity

    viewPager = (ViewPager) findViewById(R.id.viewpager);
    viewPager.setOffscreenPageLimit(3);
    setupViewPager(viewPager);

    //Initializing the tablayout
    tabLayout = (TabLayout) findViewById(R.id.tablayout);
    tabLayout.setupWithViewPager(viewPager);

    try
    {
        setupTabIcons();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

Fifth Define setupTabIcons and prepareTabView methods

  private void setupTabIcons()
{

    for(int i=0;i<tabTitle.length;i++)
    {
        /*TabLayout.Tab tabitem = tabLayout.newTab();
        tabitem.setCustomView(prepareTabView(i));
        tabLayout.addTab(tabitem);*/

        tabLayout.getTabAt(i).setCustomView(prepareTabView(i));
    }
    }

 String[] tabTitle={"LOL!","LOL@","LOL#"};
int[] unreadCount={1,3,3};

private View prepareTabView(int pos) {
    View view = getLayoutInflater().inflate(R.layout.custom_tab,null);
    TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
    TextView tv_count = (TextView) view.findViewById(R.id.tv_count);
    tv_title.setText(tabTitle[pos]);
    if(unreadCount[pos]>0)
    {
        tv_count.setVisibility(View.VISIBLE);
        tv_count.setText(""+unreadCount[pos]);
    }
    else
        tv_count.setVisibility(View.GONE);


    return view;
    }

I might have left something during answering this question, Just ping me, I would be happy to help :D

codepeaker
  • 420
  • 8
  • 15
2

Answer working in 2022

Hi there, I am bit late to answer but as of now with Material components you don't have to look for any custom view or third party library

I am using com.google.android.material.tabs.TabLayout with androidx.viewpager2.widget.ViewPager2 and it is working fine

val requestsTab = viewBinding.tabLayout.getTabAt(2)
            val orCreateBadge = requestsTab?.orCreateBadge
            orCreateBadge?.number=10
            orCreateBadge?.backgroundColor = resources.getColor(R.color.blue,context?.theme)
            orCreateBadge?.badgeTextColor =  resources.getColor(R.color.white,context?.theme)

sample

Umar Ata
  • 4,170
  • 3
  • 23
  • 35
0

just use this trick :

BadgeView bv1 = new BadgeView(this, ((ViewGroup) tabLayout.getChildAt(0)).getChildAt(0));
Navidonline
  • 98
  • 1
  • 2
  • 7
  • 8
    what is BadgeView? – behelit May 02 '16 at 00:32
  • 3
    He is referring to the [ViewBadger](https://github.com/jgilfelt/android-viewbadger/blob/master/README.markdown) Library,to add badges to views.Keep in mind this library is now deprecated. – Pugin Mar 09 '17 at 16:57
0

In kotlin, works:

if(list.isNotEmpty()) {
    mTlSaved.getTabAt(0)?.orCreateBadge?.number = list.size
}
Carlos Galindo
  • 101
  • 1
  • 6
0
binding.tabLayout.getTabAt(0)?.orCreateBadge?.number = 5
ItSNeverLate
  • 533
  • 7
  • 8
-1

I suggest use a CustomView for tabs. you can create a new view with badge and set that to your tabs.

tab.setCustomView(R.layout.badged_tab);
Majid Khosravi
  • 129
  • 2
  • 4
-8

There are easy way as well. Just append a special characters behind. It will look like whatsapp does.

➀ ➁ ➂ ➃ ➄ ➅ ➆ ➇ ➈ ➉ ➊ ➋ ➌ ➍ ➎ ➏ ➐ ➑ ➒ ➓

tabLayout.addTab(tabLayout.newTab().setText("News " + ➊));
Jason
  • 878
  • 2
  • 9
  • 21