Ian Lake 501ef8ea2a [NavigationRailView] Update inset handling for Navigation Rail & Bottom Nav
Resolves https://github.com/material-components/material-components-android/pull/2253

GIT_ORIGIN_REV_ID=b6c2270b8855e38231c29cbfd48949afd785258c
PiperOrigin-RevId: 383632982
2021-07-08 16:01:01 +00:00

976 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.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
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.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;
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.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.ripple.RippleUtils;
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();
@Nullable private ColorStateList itemRippleColor;
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));
}
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));
}
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_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) {
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
Drawable originalBackground = getBackground();
if (originalBackground instanceof ColorDrawable) {
materialShapeDrawable.setFillColor(
ColorStateList.valueOf(((ColorDrawable) originalBackground).getColor()));
}
materialShapeDrawable.initializeElevationOverlay(context);
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);
itemRippleColor = null;
}
/**
* 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);
itemRippleColor = null;
}
/**
* 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 itemRippleColor;
}
/**
* 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) {
if (this.itemRippleColor == itemRippleColor) {
// Clear the item background when setItemRippleColor(null) is called for consistency.
if (itemRippleColor == null && menuView.getItemBackground() != null) {
menuView.setItemBackground(null);
}
return;
}
this.itemRippleColor = itemRippleColor;
if (itemRippleColor == null) {
menuView.setItemBackground(null);
} else {
ColorStateList rippleDrawableColor =
RippleUtils.convertToRippleDrawableColor(itemRippleColor);
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
menuView.setItemBackground(new RippleDrawable(rippleDrawableColor, null, null));
} else {
GradientDrawable rippleDrawable = new GradientDrawable();
// TODO: Find a workaround for this. Currently on certain devices/versions, LayerDrawable
// will draw a black background underneath any layer with a non-opaque color,
// (e.g. ripple) unless we set the shape to be something that's not a perfect rectangle.
rippleDrawable.setCornerRadius(0.00001F);
Drawable rippleDrawableCompat = DrawableCompat.wrap(rippleDrawable);
DrawableCompat.setTintList(rippleDrawableCompat, rippleDrawableColor);
menuView.setItemBackground(rippleDrawableCompat);
}
}
}
/**
* 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);
}
/**
* 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);
}
/**
* 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;
}
@NonNull
protected 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];
}
};
}
}