rightnao 8b016a0e8b [NavigationRail][Badge] Added bigger padding in between items on the navigation rail when font scale is large so there is more room for the badges when they are forced inside the view bounds
Also fixed bug with navigation rail items moving when there is a badge vs when there is no longer a badge, and adds 4dp padding between icon container/active indicator and label text as per specs https://m3.material.io/components/navigation-rail/overview

PiperOrigin-RevId: 540349399
2023-06-14 12:47:57 -07:00

986 lines
33 KiB
Java

/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.material.navigation;
import com.google.android.material.R;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.appcompat.view.SupportMenuInflater;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.view.menu.MenuView;
import androidx.appcompat.widget.TintTypedArray;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.FrameLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.DimenRes;
import androidx.annotation.Dimension;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.RestrictTo;
import androidx.annotation.StyleRes;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
import androidx.customview.view.AbsSavedState;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.resources.MaterialResources;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.MaterialShapeUtils;
import com.google.android.material.shape.ShapeAppearanceModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Provides an abstract implementation of a navigation bar that can be used to implementation such
* as <a href="https://material.io/components/bottom-navigation">Bottom Navigation</a> or <a
* href="https://material.io/components/navigation-rail">Navigation rail</a>.
*
* <p>Navigation bars make it easy for users to explore and switch between top-level views in a
* single tap.
*
* <p>The bar contents can be populated by specifying a menu resource file. Each menu item title,
* icon and enabled state will be used for displaying navigation bar items. Menu items can also be
* used for programmatically selecting which destination is currently active. It can be done using
* {@code MenuItem#setChecked(true)}
*/
public abstract class NavigationBarView extends FrameLayout {
/**
* Label behaves as "labeled" when there are 3 items or less, or "selected" when there are 4 items
* or more.
*/
public static final int LABEL_VISIBILITY_AUTO = -1;
/** Label is shown on the selected navigation item. */
public static final int LABEL_VISIBILITY_SELECTED = 0;
/** Label is shown on all navigation items. */
public static final int LABEL_VISIBILITY_LABELED = 1;
/** Label is not shown on any navigation items. */
public static final int LABEL_VISIBILITY_UNLABELED = 2;
/**
* Menu Label visibility mode enum for component provide an implementation of navigation bar view.
*
* <p>The label visibility mode determines whether to show or hide labels in the navigation items.
* Setting the label visibility mode to {@link NavigationBarView#LABEL_VISIBILITY_SELECTED} sets
* the label to only show when selected, setting it to {@link
* NavigationBarView#LABEL_VISIBILITY_LABELED} sets the label to always show, and {@link
* NavigationBarView#LABEL_VISIBILITY_UNLABELED} sets the label to never show.
*
* <p>Setting the label visibility mode to {@link NavigationBarView#LABEL_VISIBILITY_AUTO} sets
* the label to behave as "labeled" when there are 3 items or less, or "selected" when there are 4
* items or more.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef(
value = {
LABEL_VISIBILITY_AUTO,
LABEL_VISIBILITY_SELECTED,
LABEL_VISIBILITY_LABELED,
LABEL_VISIBILITY_UNLABELED
})
@Retention(RetentionPolicy.SOURCE)
public @interface LabelVisibility {}
private static final int MENU_PRESENTER_ID = 1;
@NonNull private final NavigationBarMenu menu;
@NonNull private final NavigationBarMenuView menuView;
@NonNull private final NavigationBarPresenter presenter = new NavigationBarPresenter();
private MenuInflater menuInflater;
private OnItemSelectedListener selectedListener;
private OnItemReselectedListener reselectedListener;
public NavigationBarView(
@NonNull Context context,
@Nullable AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
super(wrap(context, attrs, defStyleAttr, defStyleRes), attrs, defStyleAttr);
// Ensure we are using the correctly themed context rather than the context that was passed in.
context = getContext();
/* Custom attributes */
TintTypedArray attributes =
ThemeEnforcement.obtainTintedStyledAttributes(
context,
attrs,
R.styleable.NavigationBarView,
defStyleAttr,
defStyleRes,
R.styleable.NavigationBarView_itemTextAppearanceInactive,
R.styleable.NavigationBarView_itemTextAppearanceActive);
// Create the menu.
this.menu = new NavigationBarMenu(context, this.getClass(), getMaxItemCount());
// Create the menu view.
menuView = createNavigationBarMenuView(context);
presenter.setMenuView(menuView);
presenter.setId(MENU_PRESENTER_ID);
menuView.setPresenter(presenter);
this.menu.addMenuPresenter(presenter);
presenter.initForMenu(getContext(), this.menu);
if (attributes.hasValue(R.styleable.NavigationBarView_itemIconTint)) {
menuView.setIconTintList(
attributes.getColorStateList(R.styleable.NavigationBarView_itemIconTint));
} else {
menuView.setIconTintList(
menuView.createDefaultColorStateList(android.R.attr.textColorSecondary));
}
setItemIconSize(
attributes.getDimensionPixelSize(
R.styleable.NavigationBarView_itemIconSize,
getResources()
.getDimensionPixelSize(R.dimen.mtrl_navigation_bar_item_default_icon_size)));
if (attributes.hasValue(R.styleable.NavigationBarView_itemTextAppearanceInactive)) {
setItemTextAppearanceInactive(
attributes.getResourceId(R.styleable.NavigationBarView_itemTextAppearanceInactive, 0));
}
if (attributes.hasValue(R.styleable.NavigationBarView_itemTextAppearanceActive)) {
setItemTextAppearanceActive(
attributes.getResourceId(R.styleable.NavigationBarView_itemTextAppearanceActive, 0));
}
boolean isBold =
attributes.getBoolean(R.styleable.NavigationBarView_itemTextAppearanceActiveBoldEnabled, true);
setItemTextAppearanceActiveBoldEnabled(isBold);
if (attributes.hasValue(R.styleable.NavigationBarView_itemTextColor)) {
setItemTextColor(attributes.getColorStateList(R.styleable.NavigationBarView_itemTextColor));
}
if (getBackground() == null || getBackground() instanceof ColorDrawable) {
// Add a MaterialShapeDrawable as background that supports tinting in every API level.
ViewCompat.setBackground(this, createMaterialShapeDrawableBackground(context,
ShapeAppearanceModel.builder(context, attrs, defStyleAttr, defStyleRes).build()));
}
if (attributes.hasValue(R.styleable.NavigationBarView_itemPaddingTop)) {
setItemPaddingTop(
attributes.getDimensionPixelSize(R.styleable.NavigationBarView_itemPaddingTop, 0));
}
if (attributes.hasValue(R.styleable.NavigationBarView_itemPaddingBottom)) {
setItemPaddingBottom(
attributes.getDimensionPixelSize(R.styleable.NavigationBarView_itemPaddingBottom, 0));
}
if (attributes.hasValue(R.styleable.NavigationBarView_activeIndicatorLabelPadding)) {
setActiveIndicatorLabelPadding(
attributes.getDimensionPixelSize(R.styleable.NavigationBarView_activeIndicatorLabelPadding, 0));
}
if (attributes.hasValue(R.styleable.NavigationBarView_elevation)) {
setElevation(attributes.getDimensionPixelSize(R.styleable.NavigationBarView_elevation, 0));
}
ColorStateList backgroundTint =
MaterialResources.getColorStateList(
context, attributes, R.styleable.NavigationBarView_backgroundTint);
DrawableCompat.setTintList(getBackground().mutate(), backgroundTint);
setLabelVisibilityMode(
attributes.getInteger(
R.styleable.NavigationBarView_labelVisibilityMode,
NavigationBarView.LABEL_VISIBILITY_AUTO));
int itemBackground = attributes.getResourceId(R.styleable.NavigationBarView_itemBackground, 0);
if (itemBackground != 0) {
menuView.setItemBackgroundRes(itemBackground);
} else {
setItemRippleColor(
MaterialResources.getColorStateList(
context, attributes, R.styleable.NavigationBarView_itemRippleColor));
}
int activeIndicatorStyleResId =
attributes.getResourceId(R.styleable.NavigationBarView_itemActiveIndicatorStyle, 0);
if (activeIndicatorStyleResId != 0) {
setItemActiveIndicatorEnabled(true);
@SuppressLint("CustomViewStyleable")
TypedArray activeIndicatorAttributes =
context.obtainStyledAttributes(
activeIndicatorStyleResId, R.styleable.NavigationBarActiveIndicator);
int itemActiveIndicatorWidth =
activeIndicatorAttributes.getDimensionPixelSize(
R.styleable.NavigationBarActiveIndicator_android_width, 0);
setItemActiveIndicatorWidth(itemActiveIndicatorWidth);
int itemActiveIndicatorHeight =
activeIndicatorAttributes.getDimensionPixelSize(
R.styleable.NavigationBarActiveIndicator_android_height, 0);
setItemActiveIndicatorHeight(itemActiveIndicatorHeight);
int itemActiveIndicatorMarginHorizontal =
activeIndicatorAttributes.getDimensionPixelOffset(
R.styleable.NavigationBarActiveIndicator_marginHorizontal, 0);
setItemActiveIndicatorMarginHorizontal(itemActiveIndicatorMarginHorizontal);
ColorStateList itemActiveIndicatorColor =
MaterialResources.getColorStateList(
context,
activeIndicatorAttributes,
R.styleable.NavigationBarActiveIndicator_android_color);
setItemActiveIndicatorColor(itemActiveIndicatorColor);
int shapeAppearanceResId =
activeIndicatorAttributes.getResourceId(
R.styleable.NavigationBarActiveIndicator_shapeAppearance, 0);
ShapeAppearanceModel itemActiveIndicatorShapeAppearance =
ShapeAppearanceModel.builder(context, shapeAppearanceResId, 0).build();
setItemActiveIndicatorShapeAppearance(itemActiveIndicatorShapeAppearance);
activeIndicatorAttributes.recycle();
}
if (attributes.hasValue(R.styleable.NavigationBarView_menu)) {
inflateMenu(attributes.getResourceId(R.styleable.NavigationBarView_menu, 0));
}
attributes.recycle();
addView(menuView);
this.menu.setCallback(
new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, @NonNull MenuItem item) {
if (reselectedListener != null && item.getItemId() == getSelectedItemId()) {
reselectedListener.onNavigationItemReselected(item);
return true; // item is already selected
}
return selectedListener != null && !selectedListener.onNavigationItemSelected(item);
}
@Override
public void onMenuModeChange(MenuBuilder menu) {}
});
}
@NonNull
private MaterialShapeDrawable createMaterialShapeDrawableBackground(
Context context, ShapeAppearanceModel shapeAppearanceModel) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
Drawable originalBackground = getBackground();
if (originalBackground instanceof ColorDrawable) {
materialShapeDrawable.setFillColor(
ColorStateList.valueOf(((ColorDrawable) originalBackground).getColor()));
}
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setShapeAppearanceModel(shapeAppearanceModel);
return materialShapeDrawable;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
MaterialShapeUtils.setParentAbsoluteElevation(this);
}
/**
* Sets the base elevation of this view, in pixels.
*
* @attr ref R.styleable#BottomNavigationView_elevation
*/
@Override
public void setElevation(float elevation) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
super.setElevation(elevation);
}
MaterialShapeUtils.setElevation(this, elevation);
}
/**
* Set a listener that will be notified when a navigation item is selected. This listener will
* also be notified when the currently selected item is reselected, unless an {@link
* OnItemReselectedListener} has also been set.
*
* @param listener The listener to notify
* @see #setOnItemReselectedListener(OnItemReselectedListener)
*/
public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
selectedListener = listener;
}
/**
* Set a listener that will be notified when the currently selected navigation item is reselected.
* This does not require an {@link OnItemSelectedListener} to be set.
*
* @param listener The listener to notify
* @see #setOnItemSelectedListener(OnItemSelectedListener)
*/
public void setOnItemReselectedListener(@Nullable OnItemReselectedListener listener) {
reselectedListener = listener;
}
/** Returns the {@link Menu} instance associated with this navigation bar. */
@NonNull
public Menu getMenu() {
return menu;
}
/**
* Returns the {@link MenuView} instance associated with this navigation bar.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@NonNull
public MenuView getMenuView() {
return menuView;
}
/**
* Inflate a menu resource into this navigation view.
*
* <p>Existing items in the menu will not be modified or removed.
*
* @param resId ID of a menu resource to inflate
*/
public void inflateMenu(int resId) {
presenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId, menu);
presenter.setUpdateSuspended(false);
presenter.updateMenuView(true);
}
/**
* Returns the tint which is applied to our menu items' icons.
*
* @see #setItemIconTintList(ColorStateList)
* @attr ref R.styleable#BottomNavigationView_itemIconTint
*/
@Nullable
public ColorStateList getItemIconTintList() {
return menuView.getIconTintList();
}
/**
* Set the tint which is applied to our menu items' icons.
*
* @param tint the tint to apply.
* @attr ref R.styleable#BottomNavigationView_itemIconTint
*/
public void setItemIconTintList(@Nullable ColorStateList tint) {
menuView.setIconTintList(tint);
}
/**
* Set the size to provide for the menu item icons.
*
* <p>For best image resolution, use an icon with the same size set in this method.
*
* @param iconSize the size in pixels to provide for the menu item icons
* @attr ref R.styleable#BottomNavigationView_itemIconSize
*/
public void setItemIconSize(@Dimension int iconSize) {
menuView.setItemIconSize(iconSize);
}
/**
* Set the size to provide for the menu item icons using a resource ID.
*
* <p>For best image resolution, use an icon with the same size set in this method.
*
* @param iconSizeRes the resource ID for the size to provide for the menu item icons
* @attr ref R.styleable#BottomNavigationView_itemIconSize
*/
public void setItemIconSizeRes(@DimenRes int iconSizeRes) {
setItemIconSize(getResources().getDimensionPixelSize(iconSizeRes));
}
/**
* Returns the size provided for the menu item icons in pixels.
*
* @see #setItemIconSize(int)
* @attr ref R.styleable#BottomNavigationView_itemIconSize
*/
@Dimension
public int getItemIconSize() {
return menuView.getItemIconSize();
}
/**
* Returns colors used for the different states (normal, selected, focused, etc.) of the menu item
* text.
*
* @see #setItemTextColor(ColorStateList)
* @return the ColorStateList of colors used for the different states of the menu items text.
* @attr ref R.styleable#BottomNavigationView_itemTextColor
*/
@Nullable
public ColorStateList getItemTextColor() {
return menuView.getItemTextColor();
}
/**
* Set the colors to use for the different states (normal, selected, focused, etc.) of the menu
* item text.
*
* @see #getItemTextColor()
* @attr ref R.styleable#BottomNavigationView_itemTextColor
*/
public void setItemTextColor(@Nullable ColorStateList textColor) {
menuView.setItemTextColor(textColor);
}
/**
* Returns the background resource of the menu items.
*
* @see #setItemBackgroundResource(int)
* @attr ref R.styleable#BottomNavigationView_itemBackground
* @deprecated Use {@link #getItemBackground()} instead.
*/
@Deprecated
@DrawableRes
public int getItemBackgroundResource() {
return menuView.getItemBackgroundRes();
}
/**
* Set the background of our menu items to the given resource.
*
* <p>This will remove any ripple backgrounds created by {@link
* #setItemRippleColor(ColorStateList)}.
*
* @param resId The identifier of the resource.
* @attr ref R.styleable#BottomNavigationView_itemBackground
*/
public void setItemBackgroundResource(@DrawableRes int resId) {
menuView.setItemBackgroundRes(resId);
}
/**
* Returns the background drawable of the menu items.
*
* @see #setItemBackground(Drawable)
* @attr ref R.styleable#BottomNavigationView_itemBackground
*/
@Nullable
public Drawable getItemBackground() {
return menuView.getItemBackground();
}
/**
* Set the background of our menu items to the given drawable.
*
* <p>This will remove any ripple backgrounds created by {@link
* #setItemRippleColor(ColorStateList)}.
*
* @param background The drawable for the background.
* @attr ref R.styleable#BottomNavigationView_itemBackground
*/
public void setItemBackground(@Nullable Drawable background) {
menuView.setItemBackground(background);
}
/**
* Returns the color used to create a ripple as the background drawable of the menu items. If a
* background is set using {@link #setItemBackground(Drawable)}, this will return null.
*
* @see #setItemBackground(Drawable)
* @attr ref R.styleable#BottomNavigationView_itemRippleColor
*/
@Nullable
public ColorStateList getItemRippleColor() {
return menuView.getItemRippleColor();
}
/**
* Set the background of our menu items to be a ripple with the given colors.
*
* @param itemRippleColor The {@link ColorStateList} for the ripple. This will create a ripple
* background for menu items, replacing any background previously set by {@link
* #setItemBackground(Drawable)}.
* @attr ref R.styleable#BottomNavigationView_itemRippleColor
*/
public void setItemRippleColor(@Nullable ColorStateList itemRippleColor) {
menuView.setItemRippleColor(itemRippleColor);
}
/**
* Get the distance from the top of an item's icon/active indicator to the top of the navigation
* bar item.
*/
@Px
public int getItemPaddingTop() {
return menuView.getItemPaddingTop();
}
/**
* Set the distance from the top of an items icon/active indicator to the top of the navigation
* bar item.
*/
public void setItemPaddingTop(@Px int paddingTop) {
menuView.setItemPaddingTop(paddingTop);
}
/**
* Get the distance from the bottom of an item's label to the bottom of the navigation bar item.
*/
@Px
public int getItemPaddingBottom() {
return menuView.getItemPaddingBottom();
}
/**
* Set the distance from the bottom of an item's label to the bottom of the navigation bar item.
*/
public void setItemPaddingBottom(@Px int paddingBottom) {
menuView.setItemPaddingBottom(paddingBottom);
}
/**
* Set the distance between the active indicator container and the item's label.
*/
public void setActiveIndicatorLabelPadding(@Px int activeIndicatorLabelPadding) {
menuView.setActiveIndicatorLabelPadding(activeIndicatorLabelPadding);
}
/**
* Get the distance between the active indicator container and the item's label.
*/
@Px
public int getActiveIndicatorLabelPadding() {
return menuView.getActiveIndicatorLabelPadding();
}
/**
* Get whether or not a selected item should show an active indicator.
*
* @return true if an active indicator will be shown when an item is selected.
*/
public boolean isItemActiveIndicatorEnabled() {
return menuView.getItemActiveIndicatorEnabled();
}
/**
* Set whether a selected item should show an active indicator.
*
* @param enabled true if a selected item should show an active indicator.
*/
public void setItemActiveIndicatorEnabled(boolean enabled) {
menuView.setItemActiveIndicatorEnabled(enabled);
}
/**
* Get the width of an item's active indicator.
*
* @return The width, in pixels, of a menu item's active indicator.
*/
@Px
public int getItemActiveIndicatorWidth() {
return menuView.getItemActiveIndicatorWidth();
}
/**
* Set the width of an item's active indicator.
*
* @param width The width, in pixels, of the menu item's active indicator.
*/
public void setItemActiveIndicatorWidth(@Px int width) {
menuView.setItemActiveIndicatorWidth(width);
}
/**
* Get the width of an item's active indicator.
*
* @return The width, in pixels, of a menu item's active indicator.
*/
@Px
public int getItemActiveIndicatorHeight() {
return menuView.getItemActiveIndicatorHeight();
}
/**
* Set the height of an item's active indicator.
*
* @param height The height, in pixels, of the menu item's active indicator.
*/
public void setItemActiveIndicatorHeight(@Px int height) {
menuView.setItemActiveIndicatorHeight(height);
}
/**
* Get the margin that will be maintained at the start and end of the active indicator away from
* the edges of its parent container.
*
* @return The horizontal margin, in pixels.
*/
@Px
public int getItemActiveIndicatorMarginHorizontal() {
return menuView.getItemActiveIndicatorMarginHorizontal();
}
/**
* Set the horizontal margin that will be maintained at the start and end of the active indicator,
* making sure the indicator remains the given distance from the edge of its parent container.
*
* @param horizontalMargin The horizontal margin, in pixels.
*/
public void setItemActiveIndicatorMarginHorizontal(@Px int horizontalMargin) {
menuView.setItemActiveIndicatorMarginHorizontal(horizontalMargin);
}
/**
* Get the {@link ShapeAppearanceModel} of the active indicator drawable.
*
* @return The {@link ShapeAppearanceModel} of the active indicator drawable.
*/
@Nullable
public ShapeAppearanceModel getItemActiveIndicatorShapeAppearance() {
return menuView.getItemActiveIndicatorShapeAppearance();
}
/**
* Set the {@link ShapeAppearanceModel} of the active indicator drawable.
*
* @param shapeAppearance The {@link ShapeAppearanceModel} of the active indicator drawable.
*/
public void setItemActiveIndicatorShapeAppearance(
@Nullable ShapeAppearanceModel shapeAppearance) {
menuView.setItemActiveIndicatorShapeAppearance(shapeAppearance);
}
/**
* Get the color of the active indicator drawable.
*
* @return A {@link ColorStateList} used as the color of the active indicator.
*/
@Nullable
public ColorStateList getItemActiveIndicatorColor() {
return menuView.getItemActiveIndicatorColor();
}
/**
* Set the {@link ColorStateList} of the active indicator drawable.
*
* @param csl The {@link ColorStateList} used as the color of the active indicator.
*/
public void setItemActiveIndicatorColor(@Nullable ColorStateList csl) {
menuView.setItemActiveIndicatorColor(csl);
}
/**
* Returns the currently selected menu item ID, or zero if there is no menu.
*
* @see #setSelectedItemId(int)
*/
@IdRes
public int getSelectedItemId() {
return menuView.getSelectedItemId();
}
/**
* Set the selected menu item ID. This behaves the same as tapping on an item.
*
* @param itemId The menu item ID. If no item has this ID, the current selection is unchanged.
* @see #getSelectedItemId()
*/
public void setSelectedItemId(@IdRes int itemId) {
MenuItem item = menu.findItem(itemId);
if (item != null) {
if (!menu.performItemAction(item, presenter, 0)) {
item.setChecked(true);
}
}
}
/**
* Sets the navigation items' label visibility mode.
*
* <p>The label is either always shown, never shown, or only shown when activated. Also supports
* "auto" mode, which uses the item count to determine whether to show or hide the label.
*
* @attr ref com.google.android.material.R.styleable#NavigationBarView_labelVisibilityMode
* @param labelVisibilityMode mode which decides whether or not the label should be shown. Can be
* one of {@link NavigationBarView#LABEL_VISIBILITY_AUTO}, {@link
* NavigationBarView#LABEL_VISIBILITY_SELECTED}, {@link
* NavigationBarView#LABEL_VISIBILITY_LABELED}, or {@link
* NavigationBarView#LABEL_VISIBILITY_UNLABELED}
* @see #getLabelVisibilityMode()
*/
public void setLabelVisibilityMode(@LabelVisibility int labelVisibilityMode) {
if (menuView.getLabelVisibilityMode() != labelVisibilityMode) {
menuView.setLabelVisibilityMode(labelVisibilityMode);
presenter.updateMenuView(false);
}
}
/**
* Returns the current label visibility mode used by this {@link NavigationBarView}.
*
* @attr ref com.google.android.material.R.styleable#BottomNavigationView_labelVisibilityMode
* @see #setLabelVisibilityMode(int)
*/
@NavigationBarView.LabelVisibility
public int getLabelVisibilityMode() {
return menuView.getLabelVisibilityMode();
}
/**
* Sets the text appearance to be used for inactive menu item labels.
*
* @param textAppearanceRes the text appearance ID used for inactive menu item labels
*/
public void setItemTextAppearanceInactive(@StyleRes int textAppearanceRes) {
menuView.setItemTextAppearanceInactive(textAppearanceRes);
}
/**
* Returns the text appearance used for inactive menu item labels.
*
* @return the text appearance ID used for inactive menu item labels
*/
@StyleRes
public int getItemTextAppearanceInactive() {
return menuView.getItemTextAppearanceInactive();
}
/**
* Sets the text appearance to be used for the menu item labels.
*
* @param textAppearanceRes the text appearance ID used for menu item labels
*/
public void setItemTextAppearanceActive(@StyleRes int textAppearanceRes) {
menuView.setItemTextAppearanceActive(textAppearanceRes);
}
/**
* Sets whether the active menu item labels are bold.
*
* @param isBold whether the active menu item labels are bold
*/
public void setItemTextAppearanceActiveBoldEnabled(boolean isBold) {
menuView.setItemTextAppearanceActiveBoldEnabled(isBold);
}
/**
* Returns the text appearance used for the active menu item label.
*
* @return the text appearance ID used for the active menu item label
*/
@StyleRes
public int getItemTextAppearanceActive() {
return menuView.getItemTextAppearanceActive();
}
/**
* Sets an {@link android.view.View.OnTouchListener} for the item view associated with the
* provided {@code menuItemId}.
*/
public void setItemOnTouchListener(int menuItemId, @Nullable OnTouchListener onTouchListener) {
menuView.setItemOnTouchListener(menuItemId, onTouchListener);
}
/**
* Returns an instance of {@link BadgeDrawable} associated with {@code menuItemId}, null if none
* was initialized.
*
* @param menuItemId Id of the menu item.
* @return an instance of BadgeDrawable associated with {@code menuItemId} or null.
* @see #getOrCreateBadge(int)
*/
@Nullable
public BadgeDrawable getBadge(int menuItemId) {
return menuView.getBadge(menuItemId);
}
/**
* Creates an instance of {@link BadgeDrawable} associated with {@code menuItemId} if none exists.
* Initializes (if needed) and returns the associated instance of {@link BadgeDrawable} associated
* with {@code menuItemId}.
*
* @param menuItemId Id of the menu item.
* @return an instance of BadgeDrawable associated with {@code menuItemId}.
*/
@NonNull
public BadgeDrawable getOrCreateBadge(int menuItemId) {
return menuView.getOrCreateBadge(menuItemId);
}
/**
* Removes the {@link BadgeDrawable} associated with {@code menuItemId}. Do nothing if none
* exists. Consider changing the visibility of the {@link BadgeDrawable} if you only want to hide
* it temporarily.
*
* @param menuItemId Id of the menu item.
*/
public void removeBadge(int menuItemId) {
menuView.removeBadge(menuItemId);
}
/** Listener for handling selection events on navigation items. */
public interface OnItemSelectedListener {
/**
* Called when an item in the navigation menu is selected.
*
* @param item The selected item
* @return true to display the item as the selected item and false if the item should not be
* selected. Consider setting non-selectable items as disabled preemptively to make them
* appear non-interactive.
*/
boolean onNavigationItemSelected(@NonNull MenuItem item);
}
/** Listener for handling reselection events on navigation items. */
public interface OnItemReselectedListener {
/**
* Called when the currently selected item in the navigation menu is selected again.
*
* @param item The selected item
*/
void onNavigationItemReselected(@NonNull MenuItem item);
}
/** Returns the maximum number of items that can be shown in NavigationBarView. */
public abstract int getMaxItemCount();
/**
* Returns reference to a newly created {@link NavigationBarMenuView}
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@NonNull
protected abstract NavigationBarMenuView createNavigationBarMenuView(@NonNull Context context);
private MenuInflater getMenuInflater() {
if (menuInflater == null) {
menuInflater = new SupportMenuInflater(getContext());
}
return menuInflater;
}
/**
* Returns reference to the {@link NavigationBarPresenter}
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@NonNull
public NavigationBarPresenter getPresenter() {
return presenter;
}
@Override
@NonNull
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.menuPresenterState = new Bundle();
menu.savePresenterStates(savedState.menuPresenterState);
return savedState;
}
@Override
protected void onRestoreInstanceState(@Nullable Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
menu.restorePresenterStates(savedState.menuPresenterState);
}
static class SavedState extends AbsSavedState {
@Nullable Bundle menuPresenterState;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(@NonNull Parcel source, ClassLoader loader) {
super(source, loader);
if (loader == null) {
loader = getClass().getClassLoader();
}
readFromParcel(source, loader);
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeBundle(menuPresenterState);
}
private void readFromParcel(@NonNull Parcel in, ClassLoader loader) {
menuPresenterState = in.readBundle(loader);
}
public static final Creator<SavedState> CREATOR =
new ClassLoaderCreator<SavedState>() {
@NonNull
@Override
public SavedState createFromParcel(@NonNull Parcel in, ClassLoader loader) {
return new SavedState(in, loader);
}
@Nullable
@Override
public SavedState createFromParcel(@NonNull Parcel in) {
return new SavedState(in, null);
}
@NonNull
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}