Значок на BottomNavigationView

Я пытаюсь добавить значок к элементу BottomNavigationView без использования какой-либо библиотеки, однако каким-то образом BottomNavigationView не показывает значок (custom_view)

main_view.xml:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context="com.hrskrs.test.MainActivity">

  <FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  <android.support.design.widget.BottomNavigationView
    android:id="@+id/bottom_navigation"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    app:itemBackground="@color/colorPrimary"
    app:itemIconTint="@color/colorAccent"
    app:itemTextColor="@color/colorPrimaryDark"
    app:menu="@menu/bottom_navigation_main" />
</RelativeLayout>

bottom_navigation_menu.xml:

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <item
    android:id="@+id/item_test"
    android:icon="@mipmap/ic_launcher"
    android:title="action1"
    app:showAsAction="always" />

  <item
    android:enabled="true"
    android:icon="@mipmap/ic_launcher"
    android:title="action2"
    app:showAsAction="ifRoom" />

  <item
    android:enabled="true"
    android:icon="@mipmap/ic_launcher"
    android:title="action3"
    app:showAsAction="ifRoom" />
</menu>

Активность продлена с AppCompatActivity:

@Override
  public boolean onCreateOptionsMenu(Menu menu) {
    menu = bottomNavigationView.getMenu();
    menu.clear();
    getMenuInflater().inflate(R.menu.bottom_navigation_main, menu);
    MenuItem item = menu.findItem(R.id.item_test);
    item = MenuItemCompat.setActionView(item, R.layout.custom_view);
    RelativeLayout badgeWrapper = (RelativeLayout) MenuItemCompat.getActionView(item);

    TextView textView = (TextView) badgeWrapper.findViewById(R.id.txtCount);
    textView.setText("99+");

    return super.onCreateOptionsMenu(menu);
  }

custom_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  style="@android:style/Widget.ActionButton"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@android:color/transparent"
  android:clickable="true"
  android:gravity="center"
  android:orientation="vertical">

  <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="Notification Icon"
    android:gravity="center"
    android:src="@mipmap/ic_launcher" />

  <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txtCount"
    android:gravity="right"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/ic_badge"
    android:text="55"
    android:textColor="#ffffffff"
    android:textSize="12sp" />
</RelativeLayout>

Вместо того, чтобы показывать (badge) custom_view, он показывает только сам элемент:

введите описание изображения здесь

Ниже из режима отладки видно, что view, к которому был осуществлен доступ, является правильным и устанавливается правильно. Однако каким-то образом BottomNavigationView не аннулируется:

введите описание изображения здесь


comment
Вы нашли обходной путь для этого ??   -  person Moulesh    schedule 25.07.2017
comment
Я ответил на этот вопрос здесь stackoverflow.com/questions/42682855/. Надеюсь, это поможет.   -  person Arash    schedule 06.10.2017
comment
На этот вопрос уже был дан ответ здесь   -  person Abel    schedule 10.06.2019
comment
stackoverflow.com/a/57073610/7254873   -  person Sumit Shukla    schedule 26.07.2019


Ответы (4)


Вы можете использовать BottomNavigationView предоставляется Библиотекой компонентов материалов.

Просто добавьте BottomNavigationView в свой макет:

  <com.google.android.material.bottomnavigation.BottomNavigationView
      android:layout_gravity="bottom"
      app:menu="@menu/navigation_main" 
      ../>

Затем используйте в своем коде:

  int menuItemId = bottomNavigationView.getMenu().getItem(0).getItemId();
  BadgeDrawable badge = bottomNavigationView.getOrCreateBadge(menuItemId);
  badge.setNumber(2);

введите описание изображения здесь

Чтобы изменить гравитацию значка, используйте метод setBadgeGravity.

badge.setBadgeGravity(BadgeDrawable.BOTTOM_END);

введите описание изображения здесь

person Gabriele Mariotti    schedule 07.10.2019

Удалось сделать BottomNavigationView со значком. Вот мой код (Котлин).

Это панель (унаследованная от BottomNavigationView)

/** Bottom menu with badge */
class AdvancedBottomNavigationView(context: Context, attrs: AttributeSet) : BottomNavigationView(context, attrs) {

    private companion object {
        const val BADGE_MIN_WIDTH = 16          // [dp]
        const val BADGE_MARGIN_TOP = 5          // [dp]
        const val BADGE_MARGIN_LEFT = 15        // [dp]
    }

    @Inject internal lateinit var uiCalculator: UICalculatorInterface

    private val bottomMenuView: BottomNavigationMenuView

    init {
        //  Get access to internal menu
        val field = BottomNavigationView::class.java.getDeclaredField("mMenuView")
        field.isAccessible = true
        bottomMenuView = field.get(this) as BottomNavigationMenuView

        App.injections.presentationLayerComponent!!.inject(this)

        @SuppressLint("CustomViewStyleable")
        val a = context.obtainStyledAttributes(attrs, R.styleable.advanced_bottom_navigation_bar)
        val badgeLayoutId  = a.getResourceId(R.styleable.advanced_bottom_navigation_bar_badge_layout, -1)
        a.recycle()

        initBadges(badgeLayoutId)
    }

    /**
     * [position] index of menu item */
    fun setBadgeValue(position: Int, count: Int) {
        val menuView = bottomMenuView
        val menuItem = menuView.getChildAt(position) as BottomNavigationItemView

        val badge = menuItem.findViewById(R.id.bottom_bar_badge)
        val badgeText = menuItem.findViewById(R.id.bottom_bar_badge_text) as TextView

        if (count > 0) {
            badgeText.text = count.toString()
            badge.visibility = View.VISIBLE
        } else {
            badge.visibility = View.GONE
        }
    }

    /**
     * Select menu item
     * [position] index of menu item to select
     */
    fun setSelected(position: Int) = bottomMenuView.getChildAt(position).performClick()

    private fun initBadges(badgeLayoutId: Int) {
        // Adding badges to each Item
        val menuView = bottomMenuView
        val totalItems = menuView.childCount

        val oneItemAreaWidth = uiCalculator.getScreenSize(context).first / totalItems

        val marginTop = uiCalculator.dpToPixels(context, BADGE_MARGIN_TOP)
        val marginLeft = uiCalculator.dpToPixels(context, BADGE_MARGIN_LEFT)

        for (i in 0 until totalItems) {
            val menuItem = menuView.getChildAt(i) as BottomNavigationItemView

            // Add badge to every item
            val badge = View.inflate(context, badgeLayoutId, null) as FrameLayout
            badge.visibility = View.GONE
            badge.minimumWidth = uiCalculator.dpToPixels(context, BADGE_MIN_WIDTH)

            val layoutParam = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT)
            layoutParam.gravity = Gravity.START

            layoutParam.setMargins(oneItemAreaWidth / 2 + marginLeft, marginTop, 0, 0)
            menuItem.addView(badge, layoutParam)
        }
    }
 }

Это файл attr.xml с параметрами для этого компонента:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="advanced_bottom_navigation_bar">
        <attr name="badge_layout" format="reference|integer" />
    </declare-styleable>
</resources>

Фон для значка из папки с возможностью переноса:

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

Сам значок:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/bottom_bar_badge"
    android:layout_height="20dp"
    android:layout_width="20dp"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@drawable/bcg_badge"
    >
<TextView
    android:id="@+id/bottom_bar_badge_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="1"
    android:textSize="10sp"
    android:textColor="@android:color/white"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:textAlignment="center"
    android:layout_gravity="center"/>
</FrameLayout>

А это пример того, как использовать это в вашем коде:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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"
    tools:context="su.ivcs.ucim.presentationLayer.userStories.mainScreen.view.MainActivity">

    <su.ivcs.ucim.presentationLayer.common.advancedBottomNavigationView.AdvancedBottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        app:itemBackground="@android:color/white"
        app:itemIconTint="@color/main_screen_tabs_menu_items"
        app:itemTextColor="@color/main_screen_tabs_menu_items"
        app:menu="@menu/main_screen_tabs_menu"
        app:badge_layout = "@layout/common_badge"

        app:layout_constraintTop_toBottomOf="@+id/fragmentsContainer"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />

</android.support.constraint.ConstraintLayout>

Я надеюсь, это поможет вам.

person Alex Shevelev    schedule 25.10.2017
comment
что такое UICalculatorInterface ?? - person Muneerah Rashed; 01.03.2019
comment
@MuneerahRashed Это мой помощник для вычислений UI-мер - размеров и т. Д. Для этого решения это не обязательно. - person Alex Shevelev; 04.03.2019

@hrskrs Попробуйте добавить более высокую отметку к вашему txtCount или самому badgeWrapper. BottomNavigationView, кажется, находится выше, чем виды на экране.

У меня возникли проблемы с отображением значков на BottomNavigationView элементах. Мой значок (без какого-либо текстового значения), являющийся частью самого рисованного объекта, стал серым, когда пользователь щелкнул другой элемент, или стал того же цвета, что и в оттенке (если не определено, это colorPrimary). Я думаю, вы столкнетесь с той же проблемой, с которой я столкнулся с раскрашиванием значка / счетчика в верхней части пункта меню BottomNavigationView, поскольку цвет оттенка будет применен к самому элементу, а ваш badgeWrapper, являющийся частью MenuItem, примет оттенок (станет серым, когда вы коснитесь любого другого предмета, который, я думаю, вам не нужен).

Посмотрите мой ответ здесь: Есть ли способ отображения значка уведомления в официальных пунктах меню Google BottomNavigationView, представленных в API 25?

Я использовал ImageView для значка, но вы можете использовать свой badgeWrapper RelativeView вместо ImageView.

person Waqas    schedule 25.02.2017
comment
Я не прошу альтернативы, поскольку есть несколько способов добиться этого. Прошу добавить значок с BottomNavigationView по Menu resources - person hrskrs; 27.02.2017
comment
Я боролся с ресурсами меню, и у меня это не сработало. Правильно было бы делать это с ресурсами меню. Буду рад видеть, получится ли у вас работать с ресурсами меню. - person Waqas; 27.02.2017

используйте BadgeDrawable следующим образом:

Integer amount = tabBadgeCountMap.get(tab);
            BadgeDrawable badgeDrawable = bottomNavigationView.getOrCreateBadge(tabMenuResId);
            badgeDrawable.setNumber(amount != null ? amount : 0);
person Alécio Carvalho    schedule 12.08.2019