blob: 910e667fb30bdcf8581a310a6a3c378095bda8fc [file] [log] [blame]
/*
* 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 android.support.design.widget;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.R;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.internal.BottomNavigationPresenter;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.view.SupportMenuInflater;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.widget.TintTypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* Represents a standard bottom navigation bar for application. It is an implementation of material
* design bottom navigation. See https://material.google.com/components/bottom-navigation.html
*
* Bottom navigation bars make it easy for users to explore and switch between top-level views in
* a single tap. It should be used when application has three to five top-level destinations.
*
* 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.
*
* <android.support.design.widget.BottomNavigationView
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:app="http://schemas.android.com/apk/res-auto"
* android:id="@+id/navigation"
* android:layout_width="wrap_content"
* android:layout_height="match_parent"
* android:layout_gravity="start"
* app:menu="@menu/my_navigation_items" />
*/
public class BottomNavigationView extends FrameLayout {
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
private final MenuBuilder mMenu;
private final BottomNavigationMenuView mMenuView;
private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter();
private MenuInflater mMenuInflater;
private OnNavigationItemSelectedListener mListener;
public BottomNavigationView(Context context) {
this(context, null);
}
public BottomNavigationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ThemeUtils.checkAppCompatTheme(context);
// Create the menu
mMenu = new MenuBuilder(context);
mMenuView = new BottomNavigationMenuView(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mMenuView.setLayoutParams(params);
mPresenter.setBottomNavigationMenuView(mMenuView);
mMenuView.setPresenter(mPresenter);
mMenu.addMenuPresenter(mPresenter);
// Custom attributes
TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
R.styleable.BottomNavigationView, defStyleAttr,
R.style.Widget_Design_BottomNavigationView);
if (a.hasValue(R.styleable.BottomNavigationView_itemIconTint)) {
mMenuView.setIconTintList(
a.getColorStateList(R.styleable.BottomNavigationView_itemIconTint));
} else {
mMenuView.setIconTintList(
createDefaultColorStateList(android.R.attr.textColorSecondary));
}
if (a.hasValue(R.styleable.BottomNavigationView_itemTextColor)) {
mMenuView.setItemTextColor(
a.getColorStateList(R.styleable.BottomNavigationView_itemTextColor));
} else {
mMenuView.setItemTextColor(
createDefaultColorStateList(android.R.attr.textColorSecondary));
}
int itemBackground = a.getResourceId(R.styleable.BottomNavigationView_itemBackground, 0);
mMenuView.setItemBackgroundRes(itemBackground);
if (a.hasValue(R.styleable.BottomNavigationView_menu)) {
inflateMenu(a.getResourceId(R.styleable.BottomNavigationView_menu, 0));
}
a.recycle();
addView(mMenuView);
mMenu.setCallback(new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
return mListener != null && mListener.onNavigationItemSelected(item);
}
@Override
public void onMenuModeChange(MenuBuilder menu) {}
});
}
/**
* Set a listener that will be notified when a bottom navigation item is selected.
*
* @param listener The listener to notify
*/
public void setOnNavigationItemSelectedListener(
@Nullable OnNavigationItemSelectedListener listener) {
mListener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO(aurimas): move updateOnSizeChange to a different location that is less expensive.
mMenuView.updateOnSizeChange(MeasureSpec.getSize(widthMeasureSpec));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mMenuView.updateOnSizeChange(w);
}
/**
* Returns the {@link Menu} instance associated with this bottom navigation bar.
*/
@NonNull
public Menu getMenu() {
return mMenu;
}
/**
* Inflate a menu resource into this navigation view.
*
* <p>Existing items in the menu will not be modified or removed.</p>
*
* @param resId ID of a menu resource to inflate
*/
public void inflateMenu(int resId) {
mPresenter.setUpdateSuspended(true);
getMenuInflater().inflate(resId, mMenu);
mPresenter.initForMenu(getContext(), mMenu);
mPresenter.setUpdateSuspended(false);
mPresenter.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 mMenuView.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) {
mMenuView.setIconTintList(tint);
}
/**
* Returns the tint which is applied to menu items' icons.
*
* @see #setItemTextColor(ColorStateList)
*
* @attr ref R.styleable#BottomNavigationView_itemTextColor
*/
@Nullable
public ColorStateList getItemTextColor() {
return mMenuView.getItemTextColor();
}
/**
* Set the text color to be used on menu items.
*
* @see #getItemTextColor()
*
* @attr ref R.styleable#BottomNavigationView_itemTextColor
*/
public void setItemTextColor(@Nullable ColorStateList textColor) {
mMenuView.setItemTextColor(textColor);
}
/**
* Set the background of our menu items to the given resource.
*
* @param resId The identifier of the resource.
*
* @attr ref R.styleable#BottomNavigationView_itemBackground
*/
public void setItemBackgroundResource(@DrawableRes int resId) {
mMenuView.setItemBackgroundRes(resId);
}
/**
* Listener for handling events on bottom navigation items.
*/
public interface OnNavigationItemSelectedListener {
/**
* Called when an item in the bottom navigation menu is selected.
*
* @param item The selected item
*
* @return true to display the item as the selected item
*/
public boolean onNavigationItemSelected(@NonNull MenuItem item);
}
private MenuInflater getMenuInflater() {
if (mMenuInflater == null) {
mMenuInflater = new SupportMenuInflater(getContext());
}
return mMenuInflater;
}
private ColorStateList createDefaultColorStateList(int baseColorThemeAttr) {
final TypedValue value = new TypedValue();
if (!getContext().getTheme().resolveAttribute(baseColorThemeAttr, value, true)) {
return null;
}
ColorStateList baseColor = AppCompatResources.getColorStateList(
getContext(), value.resourceId);
if (!getContext().getTheme().resolveAttribute(
android.support.v7.appcompat.R.attr.colorPrimary, value, true)) {
return null;
}
int colorPrimary = value.data;
int defaultColor = baseColor.getDefaultColor();
return new ColorStateList(new int[][]{
DISABLED_STATE_SET,
CHECKED_STATE_SET,
EMPTY_STATE_SET
}, new int[]{
baseColor.getColorForState(DISABLED_STATE_SET, defaultColor),
colorPrimary,
defaultColor
});
}
}