219 lines
8.7 KiB
Java

/*
* Copyright (C) 2016 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.bottomnavigation;
import com.google.android.material.R;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.content.Context;
import android.os.Build.VERSION;
import androidx.appcompat.widget.TintTypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.internal.ThemeEnforcement;
import com.google.android.material.navigation.NavigationBarMenuView;
import com.google.android.material.navigation.NavigationBarView;
import com.google.android.material.shape.MaterialShapeDrawable;
/**
* Represents a standard bottom navigation bar for application. It is an implementation of <a
* href="https://material.google.com/components/bottom-navigation.html">material design bottom
* navigation</a>.
*
* <p>Bottom navigation bars make it easy for users to explore and switch between top-level views in
* a single tap. They should be used when an application has three to five top-level destinations.
*
* <p>The bar can disappear on scroll, based on {@link
* com.google.android.material.behavior.HideBottomViewOnScrollBehavior}, when it is placed within a
* {@link CoordinatorLayout} and one of the children within the {@link CoordinatorLayout} is
* scrolled. This behavior is only set if the {@code layout_behavior} property is set to {@link
* HideBottomViewOnScrollBehavior}.
*
* <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 bottom 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)}
*
* <pre>
* layout resource file:
* &lt;com.google.android.material.bottomnavigation.BottomNavigationView
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schema.android.com/apk/res/res-auto"
* android:id="@+id/navigation"
* android:layout_width="match_parent"
* android:layout_height="56dp"
* android:layout_gravity="start"
* app:menu="@menu/my_navigation_items" /&gt;
*
* res/menu/my_navigation_items.xml:
* &lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
* &lt;item android:id="@+id/action_search"
* android:title="@string/menu_search"
* android:icon="@drawable/ic_search" /&gt;
* &lt;item android:id="@+id/action_settings"
* android:title="@string/menu_settings"
* android:icon="@drawable/ic_add" /&gt;
* &lt;item android:id="@+id/action_navigation"
* android:title="@string/menu_navigation"
* android:icon="@drawable/ic_action_navigation_menu" /&gt;
* &lt;/menu&gt;
* </pre>
*/
public class BottomNavigationView extends NavigationBarView {
static final int MAX_ITEM_COUNT = 5;
public BottomNavigationView(@NonNull Context context) {
this(context, null);
}
public BottomNavigationView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.bottomNavigationStyle);
}
public BottomNavigationView(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.Widget_Design_BottomNavigationView);
}
public BottomNavigationView(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// 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.BottomNavigationView, defStyleAttr, defStyleRes);
setItemHorizontalTranslationEnabled(
attributes.getBoolean(
R.styleable.BottomNavigationView_itemHorizontalTranslationEnabled, true));
attributes.recycle();
if (shouldDrawCompatibilityTopDivider()) {
addCompatibilityTopDivider(context);
}
}
/**
* Sets whether the menu items horizontally translate on selection when the combined item widths
* fill up the screen.
*
* @param itemHorizontalTranslationEnabled whether the items horizontally translate on selection
* @see #isItemHorizontalTranslationEnabled()
*/
public void setItemHorizontalTranslationEnabled(boolean itemHorizontalTranslationEnabled) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) getMenuView();
if (menuView.isItemHorizontalTranslationEnabled() != itemHorizontalTranslationEnabled) {
menuView.setItemHorizontalTranslationEnabled(itemHorizontalTranslationEnabled);
getPresenter().updateMenuView(false);
}
}
/**
* Returns whether the items horizontally translate on selection when the item widths fill up the
* screen.
*
* @return whether the menu items horizontally translate on selection
* @see #setItemHorizontalTranslationEnabled(boolean)
*/
public boolean isItemHorizontalTranslationEnabled() {
return ((BottomNavigationMenuView) getMenuView()).isItemHorizontalTranslationEnabled();
}
@Override
public int getMaxItemCount() {
return MAX_ITEM_COUNT;
}
/** @hide */
@RestrictTo(LIBRARY_GROUP)
@Override
@NonNull
protected NavigationBarMenuView createNavigationBarMenuView(@NonNull Context context) {
return new BottomNavigationMenuView(context);
}
/**
* Returns true a divider must be added in place of shadows to maintain compatibility in pre-21
* legacy backgrounds.
*/
private boolean shouldDrawCompatibilityTopDivider() {
return VERSION.SDK_INT < 21 && !(getBackground() instanceof MaterialShapeDrawable);
}
/**
* Adds a divider in place of shadows to maintain compatibility in pre-21 legacy backgrounds. If a
* pre-21 background has been updated to a MaterialShapeDrawable, MaterialShapeDrawable will draw
* shadows instead.
*/
private void addCompatibilityTopDivider(@NonNull Context context) {
View divider = new View(context);
divider.setBackgroundColor(
ContextCompat.getColor(context, R.color.design_bottom_navigation_shadow_color));
FrameLayout.LayoutParams dividerParams =
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
getResources().getDimensionPixelSize(R.dimen.design_bottom_navigation_shadow_height));
divider.setLayoutParams(dividerParams);
addView(divider);
}
/**
* Set a listener that will be notified when a bottom navigation item is selected. This listener
* will also be notified when the currently selected item is reselected, unless an {@link
* OnNavigationItemReselectedListener} has also been set.
*
* @param listener The listener to notify
* @see #setOnNavigationItemReselectedListener(OnNavigationItemReselectedListener)
*/
public void setOnNavigationItemSelectedListener(
@Nullable OnNavigationItemSelectedListener listener) {
setOnItemSelectedListener(listener);
}
/**
* Set a listener that will be notified when the currently selected bottom navigation item is
* reselected. This does not require an {@link OnNavigationItemSelectedListener} to be set.
*
* @param listener The listener to notify
* @see #setOnNavigationItemSelectedListener(OnNavigationItemSelectedListener)
*/
public void setOnNavigationItemReselectedListener(
@Nullable OnNavigationItemReselectedListener listener) {
setOnItemReselectedListener(listener);
}
/** Listener for handling selection events on bottom navigation items. */
public interface OnNavigationItemSelectedListener extends OnItemSelectedListener {}
/** Listener for handling reselection events on bottom navigation items. */
public interface OnNavigationItemReselectedListener extends OnItemReselectedListener {}
}