BottomNavigationView.java revision f6a12ad68e652ba7cb2d21b9285f33a770a5e537
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.design.widget; 18 19import android.content.Context; 20import android.content.res.ColorStateList; 21import android.os.Parcelable; 22import android.support.annotation.DrawableRes; 23import android.support.annotation.NonNull; 24import android.support.annotation.Nullable; 25import android.support.design.R; 26import android.support.design.internal.BottomNavigationMenu; 27import android.support.design.internal.BottomNavigationMenuView; 28import android.support.design.internal.BottomNavigationPresenter; 29import android.support.v7.content.res.AppCompatResources; 30import android.support.v7.view.SupportMenuInflater; 31import android.support.v7.view.menu.MenuBuilder; 32import android.support.v7.widget.TintTypedArray; 33import android.util.AttributeSet; 34import android.util.TypedValue; 35import android.view.Menu; 36import android.view.MenuInflater; 37import android.view.MenuItem; 38import android.view.ViewGroup; 39import android.widget.FrameLayout; 40import android.widget.LinearLayout; 41 42/** 43 * <p> 44 * Represents a standard bottom navigation bar for application. It is an implementation of 45 * <a href="https://material.google.com/components/bottom-navigation.html">material design bottom 46 * navigation</a>. 47 * </p> 48 * 49 * <p> 50 * Bottom navigation bars make it easy for users to explore and switch between top-level views in 51 * a single tap. It should be used when application has three to five top-level destinations. 52 * </p> 53 * 54 * <p> 55 * The bar contents can be populated by specifying a menu resource file. Each menu item title, icon 56 * and enabled state will be used for displaying bottom navigation bar items. 57 * </p> 58 * 59 * <pre> 60 * <android.support.design.widget.BottomNavigationView 61 * xmlns:android="http://schemas.android.com/apk/res/android" 62 * xmlns:app="http://schemas.android.com/apk/res-auto" 63 * android:id="@+id/navigation" 64 * android:layout_width="wrap_content" 65 * android:layout_height="match_parent" 66 * android:layout_gravity="start" 67 * app:menu="@menu/my_navigation_items" /> 68 * </pre> 69 */ 70public class BottomNavigationView extends FrameLayout { 71 72 private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; 73 private static final int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled}; 74 75 private final MenuBuilder mMenu; 76 private final BottomNavigationMenuView mMenuView; 77 private final BottomNavigationPresenter mPresenter = new BottomNavigationPresenter(); 78 private MenuInflater mMenuInflater; 79 80 private OnNavigationItemSelectedListener mListener; 81 82 public BottomNavigationView(Context context) { 83 this(context, null); 84 } 85 86 public BottomNavigationView(Context context, AttributeSet attrs) { 87 this(context, attrs, 0); 88 } 89 90 public BottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) { 91 super(context, attrs, defStyleAttr); 92 93 ThemeUtils.checkAppCompatTheme(context); 94 95 // Create the menu 96 mMenu = new BottomNavigationMenu(context); 97 98 mMenuView = new BottomNavigationMenuView(context); 99 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 100 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 101 mMenuView.setLayoutParams(params); 102 103 mPresenter.setBottomNavigationMenuView(mMenuView); 104 mMenuView.setPresenter(mPresenter); 105 mMenu.addMenuPresenter(mPresenter); 106 107 108 // Custom attributes 109 TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, 110 R.styleable.BottomNavigationView, defStyleAttr, 111 R.style.Widget_Design_BottomNavigationView); 112 113 if (a.hasValue(R.styleable.BottomNavigationView_itemIconTint)) { 114 mMenuView.setIconTintList( 115 a.getColorStateList(R.styleable.BottomNavigationView_itemIconTint)); 116 } else { 117 mMenuView.setIconTintList( 118 createDefaultColorStateList(android.R.attr.textColorSecondary)); 119 } 120 if (a.hasValue(R.styleable.BottomNavigationView_itemTextColor)) { 121 mMenuView.setItemTextColor( 122 a.getColorStateList(R.styleable.BottomNavigationView_itemTextColor)); 123 } else { 124 mMenuView.setItemTextColor( 125 createDefaultColorStateList(android.R.attr.textColorSecondary)); 126 } 127 128 int itemBackground = a.getResourceId(R.styleable.BottomNavigationView_itemBackground, 0); 129 mMenuView.setItemBackgroundRes(itemBackground); 130 131 if (a.hasValue(R.styleable.BottomNavigationView_menu)) { 132 inflateMenu(a.getResourceId(R.styleable.BottomNavigationView_menu, 0)); 133 } 134 a.recycle(); 135 136 addView(mMenuView); 137 138 mMenu.setCallback(new MenuBuilder.Callback() { 139 @Override 140 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 141 return mListener != null && mListener.onNavigationItemSelected(item); 142 } 143 144 @Override 145 public void onMenuModeChange(MenuBuilder menu) {} 146 }); 147 } 148 149 /** 150 * Set a listener that will be notified when a bottom navigation item is selected. 151 * 152 * @param listener The listener to notify 153 */ 154 public void setOnNavigationItemSelectedListener( 155 @Nullable OnNavigationItemSelectedListener listener) { 156 mListener = listener; 157 } 158 159 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 160 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 161 if (mMenuView.updateOnSizeChange(getMeasuredWidth())) { 162 // updateOnSizeChanged has changed LPs, so we need to remeasure 163 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 164 } 165 } 166 167 /** 168 * Returns the {@link Menu} instance associated with this bottom navigation bar. 169 */ 170 @NonNull 171 public Menu getMenu() { 172 return mMenu; 173 } 174 175 /** 176 * Inflate a menu resource into this navigation view. 177 * 178 * <p>Existing items in the menu will not be modified or removed.</p> 179 * 180 * @param resId ID of a menu resource to inflate 181 */ 182 public void inflateMenu(int resId) { 183 mPresenter.setUpdateSuspended(true); 184 getMenuInflater().inflate(resId, mMenu); 185 mPresenter.initForMenu(getContext(), mMenu); 186 mPresenter.setUpdateSuspended(false); 187 mPresenter.updateMenuView(true); 188 } 189 190 /** 191 * @return The maximum number of items that can be shown in BottomNavigationView. 192 */ 193 public int getMaxItemCount() { 194 return BottomNavigationMenu.MAX_ITEM_COUNT; 195 } 196 197 /** 198 * Returns the tint which is applied to our menu items' icons. 199 * 200 * @see #setItemIconTintList(ColorStateList) 201 * 202 * @attr ref R.styleable#BottomNavigationView_itemIconTint 203 */ 204 @Nullable 205 public ColorStateList getItemIconTintList() { 206 return mMenuView.getIconTintList(); 207 } 208 209 /** 210 * Set the tint which is applied to our menu items' icons. 211 * 212 * @param tint the tint to apply. 213 * 214 * @attr ref R.styleable#BottomNavigationView_itemIconTint 215 */ 216 public void setItemIconTintList(@Nullable ColorStateList tint) { 217 mMenuView.setIconTintList(tint); 218 } 219 220 /** 221 * Returns the tint which is applied to menu items' icons. 222 * 223 * @see #setItemTextColor(ColorStateList) 224 * 225 * @attr ref R.styleable#BottomNavigationView_itemTextColor 226 */ 227 @Nullable 228 public ColorStateList getItemTextColor() { 229 return mMenuView.getItemTextColor(); 230 } 231 232 /** 233 * Set the text color to be used on menu items. 234 * 235 * @see #getItemTextColor() 236 * 237 * @attr ref R.styleable#BottomNavigationView_itemTextColor 238 */ 239 public void setItemTextColor(@Nullable ColorStateList textColor) { 240 mMenuView.setItemTextColor(textColor); 241 } 242 243 /** 244 * Returns the background resource of the menu items. 245 * 246 * @see #setItemBackgroundResource(int) 247 * 248 * @attr ref R.styleable#BottomNavigationView_itemBackground 249 */ 250 @DrawableRes 251 public int getItemBackgroundResource() { 252 return mMenuView.getItemBackgroundRes(); 253 } 254 255 /** 256 * Set the background of our menu items to the given resource. 257 * 258 * @param resId The identifier of the resource. 259 * 260 * @attr ref R.styleable#BottomNavigationView_itemBackground 261 */ 262 public void setItemBackgroundResource(@DrawableRes int resId) { 263 mMenuView.setItemBackgroundRes(resId); 264 } 265 266 /** 267 * Listener for handling events on bottom navigation items. 268 */ 269 public interface OnNavigationItemSelectedListener { 270 271 /** 272 * Called when an item in the bottom navigation menu is selected. 273 * 274 * @param item The selected item 275 * 276 * @return true to display the item as the selected item 277 */ 278 public boolean onNavigationItemSelected(@NonNull MenuItem item); 279 } 280 281 private MenuInflater getMenuInflater() { 282 if (mMenuInflater == null) { 283 mMenuInflater = new SupportMenuInflater(getContext()); 284 } 285 return mMenuInflater; 286 } 287 288 private ColorStateList createDefaultColorStateList(int baseColorThemeAttr) { 289 final TypedValue value = new TypedValue(); 290 if (!getContext().getTheme().resolveAttribute(baseColorThemeAttr, value, true)) { 291 return null; 292 } 293 ColorStateList baseColor = AppCompatResources.getColorStateList( 294 getContext(), value.resourceId); 295 if (!getContext().getTheme().resolveAttribute( 296 android.support.v7.appcompat.R.attr.colorPrimary, value, true)) { 297 return null; 298 } 299 int colorPrimary = value.data; 300 int defaultColor = baseColor.getDefaultColor(); 301 return new ColorStateList(new int[][]{ 302 DISABLED_STATE_SET, 303 CHECKED_STATE_SET, 304 EMPTY_STATE_SET 305 }, new int[]{ 306 baseColor.getColorForState(DISABLED_STATE_SET, defaultColor), 307 colorPrimary, 308 defaultColor 309 }); 310 } 311} 312