安卓Tablayout标签,如何添加类似于WhatsApp的通知徽章

26
我想在android.support.design.widget.TabLayout中实现通知徽章。我已尽最大努力去实现,但没有成功。

非常感谢任何帮助。


只需为选项卡创建一个相对布局,并在其中添加TextView。然后在运行时更改其文本即可。 - Nouman Ghaffar
你能否提供一个应用程序的演示给我吗?因为我已经尝试了所有可能的方法,但是还是失败了。 - Arjun sharma
发布您的选项卡代码。主选项卡活动。 - Nouman Ghaffar
将自定义视图添加到选项卡布局选项卡中,您可以轻松地从 https://github.com/jgilfelt/android-viewbadger 附加徽章。 - Benoit
Benoit,ViewBadger 对 TabLayout 无效..你可以检查一下。 - Arjun sharma
显示剩余4条评论
12个回答

39

编辑:更新

随着最新版本的Material Components发布,Android团队现在提供了官方的BadgeDrawable用于在TabLayout上实现所需的结果。它是v1.1.0-alpha07更新的一部分。https://github.com/material-components/material-components-android/releases/tag/1.1.0-alpha07

使用非常简单,可以按照以下步骤完成:

tabLayout.getTabAt(0).showBadge().setNumber(1);
请在材料组件演示应用程序中查看集成情况:https://github.com/material-components/material-components-android/commit/111cd001f5302bd6899f181ab7ccea2fd4002f63#diff-bf3e9a8a208f0ecab05088823fba99c4R239

旧答案

我尝试了上述一些解决方案,但它们没有提供正确的结果。如果您仔细查看TabLayout实现,您将意识到TabView将尝试从应用了CustomView的视图中获取textView和iconView。

因此,与其使用某些hacky实现,我提出了一个布局,它与原始TabView相等,但有一个额外的视图可以有一个徽章。

所以,您需要badged_tab.xml布局。

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

还需要一些通知背景:

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

在以编程方式添加选项卡时,只需调用

tab.setCustomView(R.layout.badged_tab);

然后您可以通过以下方式随时显示/隐藏/设置徽章计数:

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);
    }
}

1
我使用了这种方法,它对我很有效。我改编了这个解决方案,添加了一个包含徽章的自定义视图,然后当不需要在UI中时,简单地设置setCustomView(null)来移除它。创建一个具有相同间距和边距的自定义视图非常重要,这样您就可以无缝地过渡到相应选项卡的默认视图。我成功地将其与TabLayouts一起使用。或者,如果您在它们之间的UI一致性方面遇到问题,可以在所有选项卡上使用自定义视图,并根据需要在带徽章和不带徽章的布局之间切换。 - angryITguy
对于较新版本的TabLayout,您可以使用getOrCreateBadge()方法: tabLayout.getTabAt(0).getOrCreateBadge().setNumber(1); - Cjames
如何使用更新后的答案?我无法访问BadgeDrawable。我应该在我的gradle中添加哪个导入库? - luhluh
使用@android:id符号时,获取@id/text是私有的。 - user1114
“private” 错误是因为我将 text1 更改为 text。 - user1114
显示剩余5条评论

16

我建议您查看这个网站:

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

您可以使用此方法遍历不同的选项卡,并将自定义视图设置为您想要的任何内容:

  // 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;
}

}

1
我尝试了这个方法,它可以工作,但是如果例如我选择了自定义选项卡并希望该选项卡在此之后返回到其默认视图(具有正常和选定颜色),就像其他选项卡一样,我该怎么做呢? - Rashad.Z
这应该是被接受的答案。链接中有很好的解释,完美地运行。 - Shubhral
它能工作,但如何设置默认宽度和高度,就像其他选项卡一样? - vishal patel

7

14
这个库现在已经被弃用了。 - Ibrahim Gharyali
这对我有效,谢谢! - neo

5
我会使用这个:
  tabLayout.getTabAt(0).getOrCreateBadge().setNumber(10);

已弃用。在AndroidX中不再可用。 - Satyajit
@SatyajitDas TabLayout现在是Material Components for Android的一部分,而不是AndroidX。您应该切换到前者。 - TonnyL

5

这是一个老问题,但现在更加简单了。 我使用Kotlin和AndroidX,使用下一个小部件作为TabLayout:

com.google.android.material.tabs.TabLayout

我的gradle应用程序文件中:

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

对于设置简单的徽章,只需编写...

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

然后只需将 isVisible = false 设置为隐藏,就像这样:

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

对我来说可以,谢谢! - neo
Blease Java 版本 - GeneCode

3

我不知道为什么以上答案对我都不起作用 :(

我有一个自己的解决方案,可以制作出和 WhatsApp 相同带有标记的选项卡 :)

首先,创建一个名为 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>

第二个徽章背景
<?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>

第三个选项卡颜色选择器

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

在你的活动中排名第四

    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();
    }

第五步定义setupTabIcons和prepareTabView方法。
  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;
    }

如果我在回答这个问题时漏掉了什么,请联系我,我很乐意帮忙 :D


1
谢谢,就像你的情况一样,以上的解决方案都不适用于我,但是你的解决方案很好用。 - Mahdi

2

2022年的工作答案

你好,虽然我回答有点晚了,但是现在使用Material组件,您不必寻找任何自定义视图或第三方库。

我正在使用com.google.android.material.tabs.TabLayoutandroidx.viewpager2.widget.ViewPager2,它运行良好。

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


0

只需使用这个技巧:

BadgeView bv1 = new BadgeView(this, ((ViewGroup) tabLayout.getChildAt(0)).getChildAt(0));

8
BadgeView 是什么? - behelit
3
他正在提到 ViewBadger 库,用于在视图上添加徽章。请注意,该库现已过时。 - Pugin

0
binding.tabLayout.getTabAt(0)?.orCreateBadge?.number = 5

1
为了完整性,请始终解释您的答案。 - Rohit Gupta

0

在 Kotlin 中,可以正常工作:

if(list.isNotEmpty()) {
    mTlSaved.getTabAt(0)?.orCreateBadge?.number = list.size
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接