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