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