在底部导航栏图标的顶部显示徽章


77

我已经实现了我的应用程序底部的导航视图和我看过的每一个地方,以显示对喜欢的图标上方徽章这个 我不知道这是否是甚至有可能实现。任何帮助表示赞赏。谢谢。


1
当然可以。您可以在布局xml中为底部导航视图自己创建徽章。创建框架布局,然后将菜单图标放在徽标下方,并创建逻辑以显示/隐藏徽标视图。
velval

1
您可以使用BadgeView;或在github中搜索。
zxbin

@velval您有一个代码示例或教程如何实现此目标?
kalamar


thisOP中的链接是404
John

Answers:


41

修改2020:

BottomNavigation取而代之的是从材料组件中使用,它提供了在项目中添加徽章以及其他许多功能的支持:

https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md

旧答案:

使用支持库底部导航栏时,在菜单项上显示徽章/通知非常复杂。但是,有简单的解决方案可以完成它。如 https://github.com/aurelhubert/ahbottomnavigation

该库是“底部导航”栏的更高级版本。您只需使用此代码段即可在菜单项上设置徽章。

bottomNavigation.setNotification(notification, bottomNavigation.getItemsCount() - 1);

你会得到以下结果

在此处输入图片说明


工作正常,并且该库比本地的BottomNav好
Syed Usama Ahmad

1
对于2020年,请查看下面的@Abel答案,该答案显示了一种使用Google材料库中的库存视图的方法。有关更多信息:material.io/develop/android/components/bottom-navigation-view
HBB20

153

如果您只想使用股票BottomNavigationView而没有第三方库,请按照以下步骤操作:

BottomNavigationMenuView bottomNavigationMenuView =
            (BottomNavigationMenuView) navigationView.getChildAt(0);
View v = bottomNavigationMenuView.getChildAt(3); 
BottomNavigationItemView itemView = (BottomNavigationItemView) v;

View badge = LayoutInflater.from(this)
            .inflate(R.layout.notification_badge, itemView, true);

然后是布局文件:

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

    <TextView
        android:id="@+id/notifications.badge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginLeft="10dp"
        android:layout_marginStart="10dp"
        android:background="@drawable/notification_badge"
        android:gravity="center"
        android:padding="3dp"
        android:text="9+"
        android:textColor="@color/white"
        android:textSize="11sp" />
</merge>

然后只需TextView按ID查找并设置文本即可。 @drawable/notification_badge只是一个可绘制的圆形


这是没有必要来包装你TextViewFrameLayout,因为BottomNavigationItemView延伸FrameLayout
维克托·伦代纳

它的工作原理就像是一种魅力,但是我在尝试将其删除时遇到了麻烦,请问我该怎么做?
ismail alaoui 18-2-24

11
只需使用此方法即可删除徽章。 public void removeBadge(BottomNavigationView navigationView, int index) { BottomNavigationMenuView bottomNavigationMenuView = (BottomNavigationMenuView) navigationView.getChildAt(0); View v = bottomNavigationMenuView.getChildAt(index); BottomNavigationItemView itemView = (BottomNavigationItemView) v; itemView.removeViewAt(itemView.getChildCount()-1); }
Nilesh Rathore

3
我想我发现了一种从@NileshRathore答案中删除徽章的更简洁的方法。相反,itemView.removeViewAt(itemView.getChildCount()-1);我改用了:View badge = itemView.findViewById(R.id.notifications_badge); ((ViewGroup)badge.getParent()).removeView(badge);
JamTheMan '18

9
我遇到了运行时异常:android.view.InflateException:二进制XML文件第1行:<merge />只能与有效的ViewGroup根一起使用,并且attachToRoot = true
Blazej SLEBODA

67

现在本地支持添加徽章,使用最新的材料依赖关系将此添加到build.gradle

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

在您的布局中添加此

<!-- The rest of your layout here ....-->

  <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:menu="@menu/bottom_nav_menu"
            />

那你就可以

     val navBar  = findViewById<BottomNavigationView>(R.id.bottom_navigation)
     navBar.getOrCreateBadge(R.id.action_add).number = 2

R.id.action_add将成为您要添加徽章的菜单项的ID。从提供给BottomNavigationView的菜单文件中进行检查。

确保您的应用程序主题位于Theme.MaterialComponents中

您可以按样式或清单检查它。在这个例子中我的是

     <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:statusBarColor" tools:targetApi="lollipop">@color/colorPrimary</item>
    </style>

4
恕我直言,答案应该更新为此。我只是在我的应用程序中实现了这一功能,并且就像一个魅力。
奥利弗·马奥尼

5
如果您的应用程序主题尚未像我以前(Theme.AppCompat)一​​样继承自Theme.MaterialComponents,请准备对应用程序其余部分进行一些样式化。我应该首先弄清楚这一点。顺便说一句,主题更改是必需的,否则徽章将崩溃。 stackoverflow.com/questions/53476115/…–
ChrisPrime,

4
扩展我之前的评论,以保留从Theme.AppCompat.*变体迁移时的样式,只需将相应的Theme.MaterialCompoonents.*.Bridge变体直接翻译即可,而不必重新设置任何样式。 medium.com/over-engineering/…–
ChrisPrime,

1
应该被接受的答案,请使用此主题主题的变体Theme.MaterialCompoonents。*。Bridge以避免崩溃。
萨达·侯赛因

42

编辑2:
BottomNavigationView现在支持原生展示徽章,如DOC说这里

bottomNavigation.getOrCreateBadge(menuItemId)


我遇到了同样的问题,我不想使用库。

所以我创建了一个自定义布局,称为layout_news_badge

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/badge_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:id="@+id/badge_text_view"
        android:layout_width="19dp"
        android:layout_height="19dp"
        android:textSize="11sp"
        android:textColor="@android:color/white"
        android:background="@drawable/news_bottom_nav_bg"
        android:layout_gravity="top"
        android:layout_marginTop="4dp"
        android:layout_marginStart="16dp"
        android:gravity="center"
        android:padding="2dp"
        tools:text="9+" />
</FrameLayout>

TextView背景(news_bottom_nav_bg):

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:color="?attr/colorPrimary" />
</shape>

然后我BottomMenuHelper用这2种方法创建了一个:

public static void showBadge(Context context, BottomNavigationView 
    bottomNavigationView, @IdRes int itemId, String value) {
    removeBadge(bottomNavigationView, itemId);
    BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
    View badge = LayoutInflater.from(context).inflate(R.layout.layout_news_badge, bottomNavigationView, false);
    
    TextView text = badge.findViewById(R.id.badge_text_view);
    text.setText(value);
    itemView.addView(badge);
}

public static void removeBadge(BottomNavigationView bottomNavigationView, @IdRes int itemId) {
    BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
    if (itemView.getChildCount() == 3) {
        itemView.removeViewAt(2);
    }
}

然后,当我在“活动”中调用它时:

BottomMenuHelper.showBadge(this, mBottomNavigationView, R.id.action_news, "1");

编辑1: 通过建议贾廷拉纳增加了改进


1
marginTop和marginStart值是什么?

抱歉,您是对的,这些值不清楚。我编辑并用我在项目中使用的值替换了它们。谢谢您:)
ilbose

1
调用removeBadge前先调用,showBadge以避免添加重复的视图。
贾汀·拉娜(Jatin rana)

1
它与android.support.design.widget.BottomNavigationView一起使用
Roger

1
太棒了!
raphaelbgr

15

更新:现在提供材料支持徽章,请参阅下文

装袋

底部导航

val badge = bottomNavigation.getOrCreateBadge(menuItemId)
badge.isVisible = true
// An icon only badge will be displayed unless a number is set:
badge.number = 99

旧答案

基于@Tinashe的答案,我想将徽章显示为没有数字的波纹管: 在此处输入图片说明

码:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
        // position = 2
        addBadge(POSITION_HISTORY)
    }

    /**
     * add badge(notification dot) to bottom bar
     * @param position to get badge container
     */
    @SuppressLint("PrivateResource")
    private fun addBadge(position: Int) {
        // get badge container (parent)
        val bottomMenu = navigation.getChildAt(0) as? BottomNavigationMenuView
        val v = bottomMenu?.getChildAt(position) as? BottomNavigationItemView

        // inflate badge from layout
        badge = LayoutInflater.from(this)
                .inflate(R.layout.badge_layout, bottomMenu, false)

        // create badge layout parameter
        val badgeLayout: FrameLayout.LayoutParams = FrameLayout.LayoutParams(badge?.layoutParams).apply {
            gravity = Gravity.CENTER_HORIZONTAL
            topMargin = resources.getDimension(R.dimen.design_bottom_navigation_margin).toInt()

            // <dimen name="bagde_left_margin">8dp</dimen>
            leftMargin = resources.getDimension(R.dimen.bagde_left_margin).toInt()
        }

        // add view to bottom bar with layout parameter
        v?.addView(badge, badgeLayout)
    }

badge_layout.xml(badge_size = 12dp)

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/badge_size"       
    android:layout_height="@dimen/badge_size"
    android:background="@drawable/new_notification" />

和可绘制背景new_notification.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <corners android:radius="100dp"/>
    <solid android:color="#F44336"/>
</shape>

1
如何删除徽章?
Sudhir Khanger

1
@SudhirKhanger删除,为徽章视图创建全局变量。并像添加一样,在v.removeView(badge)中传递相同的内容
Narendra Pal,

13

Badge现在已由BadgeDrawable添加为AndroidX BottomNavigationView的一部分。 查看文件

fun setBadge(count: Int) {
    if (count == 0) {
        bottomNavigationView.removeBadge(R.id.ticketsNavigation)
    } else {
        val badge = bottomNavigationView.getOrCreateBadge(R.id.ticketsNavigation) // previously showBadge
        badge.number = count
        badge.backgroundColor = getColor(R.color.badge)
        badge.badgeTextColor = getColor(R.color.blackTextColor)
    }
}

// Menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/ticketsNavigation"
        android:icon="@drawable/vector_drawable_navbar_tickets"
        android:title="@string/tickets_title"/>
    ...
</menu>

编辑:

如评论中所述,应该可以只更新徽章计数,而无需一直这样添加或删除徽章:

fun setBadge(count: Int) {
    bottomNavigationView.getBadge(menuItemId)?.isVisible = count > 0
}

它在文档中,但截至2019年10月,它不属于com.google.android.material:material:1.2.0_beta1及更早版本的一部分。
Flummox-别作恶SE

@ Flummox-don'tbeevilSE好吧,我们在1.1.0-alpha07的生产环境中使用了它-所以我认为您错了她。
Morten Holmgaard '19

@MortenHomgaard,这很有趣,也许Java和Kotlin有区别吗?一直在玩这个游戏,但无法按照您在此处发布的方式进行。
Flummox-别作恶SE

showBadge已更改为getOrCreateBadge
Morten Holmgaard,

1
仅供未来读者参考。如果您只想显示/隐藏徽章,则可以使用bottom_navigation_view.getBadge(menuItemId)?. isVisible = NEW_VISIBILITY_STATE对其进行访问,而无需不断删除/创建徽章:)。
Joaquim Ley

8

作为@zxbin的答案。您可以检查BadgeView并尝试以下代码

BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
navigation.setSelectedItemId(R.id.navigation_store);
BottomNavigationMenuView bottomNavigationMenuView =
        (BottomNavigationMenuView) navigation.getChildAt(0);
View v = bottomNavigationMenuView.getChildAt(4); // number of menu from left
new QBadgeView(this).bindTarget(v).setBadgeNumber(5);

来自我的要点


嗨,如何设置徽章的右边距,使其仅在正确的位置显示
famfamfam,

文档
不足,

5

请尝试一次。

1)为徽章创建xml文件(例如,notification_badge_view.xml)

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/badge"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_gravity="top|center_horizontal"
        android:layout_marginStart="10dp"
        android:gravity="center"
        android:padding="3dp"
        app:srcCompat="@drawable/notification_badge" />
</FrameLayout>

2)为通知点形状创建可绘制文件(例如badge_circle.xml)

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@color/colorAccent" />
    <stroke
        android:width="2dp"
        android:color="@android:color/white" />
</shape>

3)在您的活动onCreate方法中,将徽章视图添加到BottomNavigationView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_landing);

         addBadgeView();

    }

4)并且addBadgeView方法如下

private void addBadgeView() {
        try {
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) bottomNavigationBar.getChildAt(0);
            BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(0); //set this to 0, 1, 2, or 3.. accordingly which menu item of the bottom bar you want to show badge
            notificationBadge = LayoutInflater.from(LandingActivity.this).inflate(R.layout.view_notification_badge, menuView, false);
            itemView.addView(notificationBadge);
            notificationBadge.setVisibility(GONE);// initially badge will be invisible 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

注意:bottomNavigationBar是您的底部栏视图。

5)通过以下方法刷新徽章以显示和隐藏

private void refreshBadgeView() {
        try {
            boolean badgeIsVisible = notificationBadge.getVisibility() != GONE;
            notificationBadge.setVisibility(badgeIsVisible ? GONE : VISIBLE);//makes badge visible and invisible 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

6)当我们点击特定的底部栏页面时,将其隐藏起来。

bottomNavigationBar.setOnNavigationItemSelectedListener(new 
BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) 
              {
                switch (menuItem.getItemId()) {
                    case R.id.bottom_bar_one:
                        //while moving to first fragment
                        notificationBadge.setVisibility(GONE);
                        break;
                    case R.id.bottom_bar_two:
                        //moving to second fragment
                        break;
                    case R.id.bottom_bar_three:
                        //moving to third fragment
                        break;
                }
                return true;
            }
        });

3

除非您已经拥有一组复杂的主题并且没有时间更改所有主题,否则@Abel的答案是最好的。

但是,如果a)时间紧迫,并且使用的是Google素材库BottomNavigationView Bar,或者b)您想添加自己的自定义视图标记,则接受的答案将无法使用 com.google.android.material:material:1.1.0

您将需要针对与接受的答案不同的视图层次进行编码

  BottomNavigationItemView itemView = (BottomNavigationItemView) ((BottomNavigationMenuView) mBottomNavigation.getChildAt(0)).getChildAt(2);
  View badge = LayoutInflater.from(this).inflate(R.layout.navigation_dot, itemView, false);
  itemView.addView(badge);

如果您确实要更新主题并更新为

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

那么您要做的就是

mBottomNavigation.getOrCreateBadge(R.id.navigation_menu_item_one).setNumber(YOUR_NUMBER);

remove和number函数仅在1.1.0-alpha09版本(及更高版本)中提供


3

一种简单的方法:

随着Material Design更新他们的资料库,并根据

https://medium.com/over-engineering/hands-on-with-material-components-for-android-bottom-navigation-aae2aa9066be

我可以在没有任何外部lib的情况下,从我的回收站适配器内部(片段中)更新(或创建BottomNavigationView徽章)。

初始状态: 初始状态

因此,就像在适配器中一样,我从我的活动中获取了上下文,然后访问它并恢复了底部导航的实例:

navBottomView = ((AppCompatActivity)this.context).findViewById(R.id.nav_view);

检查徽章是否为空(尚未创建),如果为空,则将其设置为所选数量:

  BadgeDrawable badgeDrawable = navBottomView.getBadge(R.id.navigation_carrinho);
  if (badgeDrawable == null)
      navBottomView.getOrCreateBadge(R.id.navigation_carrinho).setNumber(item.getQuantidade());

否则,获取先前的数量并增加徽章的价值:

int previousValue = badgeDrawable.getNumber();
badgeDrawable.setNumber(previousValue + item.getQuantidade());

不要忘记进口:

import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.bottomnavigation.BottomNavigationView;

最终状态:

最终状态

与添加到购物车按钮监听器合为一体:

btnAddCarrinho.setOnClickListener(v -> {
            navBottomView = ((AppCompatActivity) this.context).findViewById(R.id.nav_view);
            BadgeDrawable badgeDrawable = navBottomView.getBadge(R.id.navigation_carrinho);
            if (badgeDrawable == null) {
                navBottomView.getOrCreateBadge(R.id.navigation_carrinho).setNumber(item.getQuantidade());
            } else {
                int previousValue = badgeDrawable.getNumber();
                badgeDrawable.setNumber(previousValue + item.getQuantidade());
            }
        });

这是最好的答案。但请注意,有必要从Theme.MaterialComponents继承父主题以使用它。
porya74

2

看看文档页面:https : //material.io/develop/android/components/bottom-navigation-view/

TL; DR: 他们没有更新要使用的正确方法,因此在文档上留下了一个小错误。要添加或删除徽章,请执行以下操作:

// to remove
bottomNavigationView.removeBadge(R.id.action_settings)

// to add
bottomNavigationView.getOrCreateBadge(R.id.action_settings).apply {
    //if you want to change other attributes, like badge color, add a number, maximum number (a plus sign is added, e.g. 99+)
    number = 100
    maxCharactersCount = 3
    backgroundColor = ContextCompat.getColor(context, R.color.color_red)
}

1

使用支持库BottomNavigationView很困难。一个简单的解决方案是使用外部组件。一个易于处理的方法是:https : //github.com/roughike/BottomBar 检查其文档非常简单:

BottomBarTab nearby = bottomBar.getTabWithId(R.id.tab_nearby);
nearby.setBadgeCount(5);

// Remove the badge when you're done with it.
nearby.removeBadge/();

3
对于在2019年阅读本文档的任何人,该库都已弃用,并且两年未维护。
brandonx

没错,如今我们拥有本机BottomNavigationView,它更易于使用。
弗朗

1

您可以这样尝试:

TextView放在BottomNavigationView内进行计数(BottomNavigationViewFrameLayout):

    <android.support.design.widget.BottomNavigationView android:id="@id/bottomMenu" style="@style/bottomMenu">
        <TextView android:id="@id/bottomMenuSelectionsNumber" style="@style/bottomMenuSelectionsNumber"/>
    </android.support.design.widget.BottomNavigationView>

然后像这样设置它们的样式:

<style name="bottomMenu">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">@dimen/toolbarHeight</item>
    <item name="android:layout_gravity">center|bottom</item>
    <item name="android:background">@color/colorThird</item>
    <item name="itemBackground">@drawable/tabs_ripple</item>
    <item name="itemIconTint">@drawable/bottom_menu_item_color</item>
    <item name="itemTextColor">@drawable/bottom_menu_item_color</item>
    <item name="menu">@menu/bottom_menu</item>
</style>

<style name="bottomMenuSelectionsNumber">
    <item name="android:text">@string/bottomMenuSelectionsNumber</item>
    <item name="android:textSize">@dimen/appSecondFontSize</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:layout_width">@dimen/bottomMenuSelectionsNumberDim</item>
    <item name="android:layout_height">@dimen/bottomMenuSelectionsNumberDim</item>
    <item name="android:layout_gravity">right|bottom</item>
    <item name="android:layout_marginRight">@dimen/bottomMenuSelectionsNumberMarginR</item>
    <item name="android:layout_marginBottom">@dimen/bottomMenuSelectionsNumberMarginB</item>
    <item name="android:gravity">center</item>
    <item name="android:includeFontPadding">false</item>
    <item name="android:background">@drawable/bottom_menu_selections_number_bg</item>
</style>

bottom_menu_selections_number_bg

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:color="@color/colorAccent"/>
    <corners android:radius="@dimen/cornerRadius"/>
</shape>

0

我以这种方式做了@ilbose的一些更改答案,并测试了大大小小的屏幕尺寸

../drawable/badge_circle.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/solid_red_base" />

和../layout/notifcation_badge.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/badge_frame_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="11dp"
android:layout_gravity="center_horizontal">

<TextView
    android:id="@+id/badge_text_view"
    android:layout_width="12dp"
    android:layout_height="12dp"
    android:textSize="11sp"
    android:textColor="@android:color/white"
    android:background="@drawable/message_badge"
    android:layout_gravity="top"
    android:layout_centerHorizontal="true"
    android:padding="2dp"/> </RelativeLayout>

和在Java代码中

 public static void showBadge(Context context, BottomNavigationView
        bottomNavigationView, @IdRes int itemId, String value) {
    BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
    View badge = LayoutInflater.from(context).inflate(R.layout.message_notification_badge, bottomNavigationView, false);

    TextView text = badge.findViewById(R.id.badge_text_view);
    text.setText(value);
    itemView.addView(badge);
}

 public static void removeBadge(BottomNavigationView bottomNavigationView, @IdRes int itemId) {
    BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
    if (itemView.getChildCount() == 4) {
        itemView.removeViewAt(4);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.