BottomNavigationMenuView.java revision d38621793486c819b838030e781e94486f092012
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.internal; 18 19import android.animation.Animator; 20import android.animation.AnimatorSet; 21import android.content.Context; 22import android.content.res.ColorStateList; 23import android.os.Build; 24import android.support.annotation.Nullable; 25import android.support.design.R; 26import android.support.v4.util.Pools; 27import android.support.v7.view.menu.MenuBuilder; 28import android.support.v7.view.menu.MenuItemImpl; 29import android.support.v7.view.menu.MenuView; 30import android.util.AttributeSet; 31import android.view.Gravity; 32import android.view.View; 33import android.view.ViewGroup; 34import android.widget.LinearLayout; 35 36/** 37 * @hide 38 */ 39public class BottomNavigationMenuView extends LinearLayout implements MenuView { 40 private final int mInactiveItemMaxWidth; 41 private final int mActiveItemMaxWidth; 42 private final OnClickListener mOnClickListener; 43 private static final Pools.Pool<BottomNavigationItemView> sItemPool = 44 new Pools.SynchronizedPool<>(5); 45 46 private BottomNavigationItemView[] mButtons; 47 private int mActiveButton = 0; 48 private ColorStateList mItemIconTint; 49 private ColorStateList mItemTextColor; 50 private int mItemBackgroundRes; 51 52 private BottomNavigationPresenter mPresenter; 53 private MenuBuilder mMenu; 54 55 public BottomNavigationMenuView(Context context) { 56 this(context, null); 57 } 58 59 public BottomNavigationMenuView(Context context, AttributeSet attrs) { 60 this(context, attrs, 0); 61 } 62 63 public BottomNavigationMenuView(Context context, AttributeSet attrs, int defStyleAttr) { 64 super(context, attrs, defStyleAttr); 65 setGravity(Gravity.CENTER); 66 setOrientation(HORIZONTAL); 67 68 mInactiveItemMaxWidth = getResources().getDimensionPixelSize( 69 R.dimen.design_bottom_navigation_item_max_width); 70 mActiveItemMaxWidth = getResources() 71 .getDimensionPixelSize(R.dimen.design_bottom_navigation_active_item_max_width); 72 73 mOnClickListener = new OnClickListener() { 74 @Override 75 public void onClick(View v) { 76 final BottomNavigationItemView itemView = (BottomNavigationItemView) v; 77 final int itemPosition = itemView.getItemPosition(); 78 activateNewButton(itemPosition); 79 mMenu.performItemAction(itemView.getItemData(), mPresenter, 0); 80 } 81 }; 82 } 83 84 @Override 85 public void initialize(MenuBuilder menu) { 86 mMenu = menu; 87 if (mMenu == null) return; 88 if (mMenu.size() > mActiveButton) { 89 mMenu.getItem(mActiveButton).setChecked(true); 90 } 91 } 92 93 @Override 94 public int getWindowAnimations() { 95 return 0; 96 } 97 98 public void setIconTintList(ColorStateList color) { 99 mItemIconTint = color; 100 if (mButtons == null) return; 101 for (BottomNavigationItemView item : mButtons) { 102 item.setIconTintList(color); 103 } 104 } 105 106 @Nullable 107 public ColorStateList getIconTintList() { 108 return mItemIconTint; 109 } 110 111 public void setItemTextColor(ColorStateList color) { 112 mItemTextColor = color; 113 if (mButtons == null) return; 114 for (BottomNavigationItemView item : mButtons) { 115 item.setTextColor(color); 116 } 117 } 118 119 public ColorStateList getItemTextColor() { 120 return mItemTextColor; 121 } 122 123 public void setItemBackgroundRes(int background) { 124 mItemBackgroundRes = background; 125 if (mButtons == null) return; 126 for (BottomNavigationItemView item : mButtons) { 127 item.setItemBackground(background); 128 } 129 } 130 131 public int getItemBackgroundRes() { 132 return mItemBackgroundRes; 133 } 134 135 public void setPresenter(BottomNavigationPresenter presenter) { 136 mPresenter = presenter; 137 } 138 139 public void buildMenuView() { 140 if (mButtons != null) { 141 for (BottomNavigationItemView item : mButtons) { 142 sItemPool.release(item); 143 } 144 } 145 removeAllViews(); 146 mButtons = new BottomNavigationItemView[mMenu.size()]; 147 for (int i = 0; i < mMenu.size(); i++) { 148 mPresenter.setUpdateSuspended(true); 149 mMenu.getItem(i).setCheckable(true); 150 mPresenter.setUpdateSuspended(false); 151 BottomNavigationItemView child = getNewItem(); 152 mButtons[i] = child; 153 child.setIconTintList(mItemIconTint); 154 child.setTextColor(mItemTextColor); 155 child.setItemBackground(mItemBackgroundRes); 156 child.initialize((MenuItemImpl) mMenu.getItem(i), 0); 157 child.setItemPosition(i); 158 child.setOnClickListener(mOnClickListener); 159 addView(child); 160 } 161 } 162 163 public void updateMenuView() { 164 final int menuSize = mMenu.size(); 165 if (menuSize != mButtons.length) { 166 // The size has changed. Rebuild menu view from scratch. 167 buildMenuView(); 168 return; 169 } 170 for (int i = 0; i < menuSize; i++) { 171 mPresenter.setUpdateSuspended(true); 172 mButtons[i].initialize((MenuItemImpl) mMenu.getItem(i), 0); 173 mPresenter.setUpdateSuspended(false); 174 } 175 } 176 177 private void activateNewButton(int newButton) { 178 if (mActiveButton == newButton) return; 179 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 180 AnimatorSet animatorSet = new AnimatorSet(); 181 Animator animatorUnfocus = mButtons[mActiveButton].getAnimator(false); 182 Animator animatorFocus = mButtons[newButton].getAnimator(true); 183 if (animatorUnfocus != null) { 184 animatorSet.play(animatorUnfocus); 185 } 186 if (animatorFocus != null) { 187 animatorSet.play(animatorFocus); 188 } 189 animatorSet.start(); 190 } 191 mPresenter.setUpdateSuspended(true); 192 mButtons[mActiveButton].setChecked(false); 193 mButtons[newButton].setChecked(true); 194 mPresenter.setUpdateSuspended(false); 195 196 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 197 // Manually force UI update since we cannot use animations. 198 mPresenter.updateMenuView(true); 199 } 200 mActiveButton = newButton; 201 } 202 203 public boolean updateOnSizeChange(int width) { 204 if (getChildCount() == 0) { 205 return false; 206 } 207 int available = width / getChildCount(); 208 int itemWidth = Math.min(available, mActiveItemMaxWidth); 209 210 boolean changed = false; 211 212 for (int i = 0; i < mButtons.length; i++) { 213 ViewGroup.LayoutParams params = mButtons[i].getLayoutParams(); 214 if (params.width == itemWidth) { 215 continue; 216 } 217 changed = true; 218 params.width = itemWidth; 219 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 220 mButtons[i].setLayoutParams(params); 221 } 222 return changed; 223 } 224 225 private BottomNavigationItemView getNewItem() { 226 BottomNavigationItemView item = sItemPool.acquire(); 227 if (item == null) { 228 item = new BottomNavigationItemView(getContext()); 229 } 230 return item; 231 } 232} 233