BottomNavigationItemView.java revision 910de28f0a195668fad38ad2367c1f7237a7af98
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.animation.ValueAnimator;
22import android.annotation.TargetApi;
23import android.content.Context;
24import android.content.res.ColorStateList;
25import android.graphics.drawable.Drawable;
26import android.os.Build;
27import android.support.annotation.NonNull;
28import android.support.design.R;
29import android.support.v4.content.ContextCompat;
30import android.support.v4.graphics.drawable.DrawableCompat;
31import android.support.v4.view.animation.LinearOutSlowInInterpolator;
32import android.support.v7.view.menu.MenuItemImpl;
33import android.support.v7.view.menu.MenuView;
34import android.util.AttributeSet;
35import android.util.TypedValue;
36import android.view.Gravity;
37import android.view.LayoutInflater;
38import android.widget.ImageView;
39import android.widget.TextView;
40
41/**
42 * @hide
43 */
44public class BottomNavigationItemView extends ForegroundLinearLayout implements MenuView.ItemView {
45    public static final int INVALID_ITEM_POSTION = -1;
46
47    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
48    private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
49
50    private final int mHorizontalPadding;
51    private final int mBottomPadding;
52    private final int mTopPadding;
53    private final int mActiveTopPadding;
54    private final float mInactiveLabelSize;
55    private final float mActiveLabelSize;
56
57    private ImageView mIcon;
58    private TextView mLabel;
59    private int mItemPosition = INVALID_ITEM_POSTION;
60
61    private MenuItemImpl mItemData;
62
63    private ColorStateList mIconTint;
64    private ColorStateList mTextColor;
65
66    public BottomNavigationItemView(@NonNull Context context) {
67        this(context, null);
68    }
69
70    public BottomNavigationItemView(@NonNull Context context, AttributeSet attrs) {
71        this(context, attrs, 0);
72    }
73
74    public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
75        super(context, attrs, defStyleAttr);
76        mHorizontalPadding = getResources().getDimensionPixelSize(
77                R.dimen.design_bottom_navigation_horizontal_padding);
78        mBottomPadding = getResources().getDimensionPixelSize(
79                R.dimen.design_bottom_navigation_bottom_padding);
80        mTopPadding = getResources().getDimensionPixelSize(
81                R.dimen.design_bottom_navigation_top_padding);
82        mActiveTopPadding = getResources().getDimensionPixelSize(
83                R.dimen.design_bottom_navigation_active_top_padding);
84        mInactiveLabelSize =
85                getResources().getDimension(R.dimen.design_bottom_navigation_text_size);
86        mActiveLabelSize =
87                getResources().getDimension(R.dimen.design_bottom_navigation_active_text_size);
88
89        setOrientation(VERTICAL);
90        setGravity(Gravity.CENTER);
91        LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
92        setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
93        mIcon = (ImageView) findViewById(R.id.icon);
94        mLabel = (TextView) findViewById(R.id.label);
95    }
96
97    @Override
98    public void initialize(MenuItemImpl itemData, int menuType) {
99        mItemData = itemData;
100        setCheckable(itemData.isCheckable());
101        setChecked(itemData.isChecked());
102        setEnabled(itemData.isEnabled());
103        setIcon(itemData.getIcon());
104        setTitle(itemData.getTitle());
105        setId(itemData.getItemId());
106    }
107
108    public void setItemPosition(int position) {
109        mItemPosition = position;
110    }
111
112    public int getItemPosition() {
113        return mItemPosition;
114    }
115
116    @Override
117    public MenuItemImpl getItemData() {
118        return mItemData;
119    }
120
121    @Override
122    public void setTitle(CharSequence title) {
123        mLabel.setText(title);
124    }
125
126    @Override
127    public void setCheckable(boolean checkable) {
128        refreshDrawableState();
129    }
130
131    @Override
132    public void setChecked(boolean checked) {
133        mItemData.setChecked(checked);
134        if (checked) {
135            mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mActiveLabelSize);
136            setPadding(mHorizontalPadding, mActiveTopPadding, mHorizontalPadding, mBottomPadding);
137        } else {
138            mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, mInactiveLabelSize);
139            setPadding(mHorizontalPadding, mTopPadding, mHorizontalPadding, mBottomPadding);
140        }
141        refreshDrawableState();
142    }
143
144    @Override
145    public void setEnabled(boolean enabled) {
146        super.setEnabled(enabled);
147        mLabel.setEnabled(enabled);
148        mIcon.setEnabled(enabled);
149    }
150
151    @Override
152    public int[] onCreateDrawableState(final int extraSpace) {
153        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
154        if (mItemData != null && mItemData.isCheckable() && mItemData.isChecked()) {
155            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
156        }
157        return drawableState;
158    }
159
160    @Override
161    public void setShortcut(boolean showShortcut, char shortcutKey) {
162    }
163
164    @Override
165    public void setIcon(Drawable icon) {
166        if (icon != null) {
167            Drawable.ConstantState state = icon.getConstantState();
168            icon = DrawableCompat.wrap(state == null ? icon : state.newDrawable()).mutate();
169            DrawableCompat.setTintList(icon, mIconTint);
170        }
171        mIcon.setImageDrawable(icon);
172    }
173
174    @Override
175    public boolean prefersCondensedTitle() {
176        return false;
177    }
178
179    @Override
180    public boolean showsIcon() {
181        return true;
182    }
183
184    public void setIconTintList(ColorStateList tint) {
185        mIconTint = tint;
186        if (mItemData != null) {
187            // Update the icon so that the tint takes effect
188            setIcon(mItemData.getIcon());
189        }
190    }
191
192    public void setTextColor(ColorStateList color) {
193        mTextColor = color;
194        mLabel.setTextColor(color);
195    }
196
197    public void setItemBackground(int background) {
198        Drawable backgroundDrawable = background == 0
199                ? null : ContextCompat.getDrawable(getContext(), background);
200        setBackgroundDrawable(backgroundDrawable);
201    }
202
203    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
204    public Animator getAnimator(boolean active) {
205        final float currentTextSize = mLabel.getTextSize();
206        final int currentTopPadding = getPaddingTop();
207
208        final float finalTextSize = active ? mActiveLabelSize : mInactiveLabelSize;
209        final int finalTopPadding = active ? mActiveTopPadding : mTopPadding;
210
211        if (currentTextSize == finalTextSize && currentTopPadding == finalTopPadding) {
212            return null;
213        }
214
215        // Grow or shrink the text of the tab.
216        ValueAnimator textAnimator = ValueAnimator.ofFloat(currentTextSize, finalTextSize);
217        textAnimator.setDuration(ACTIVE_ANIMATION_DURATION_MS);
218        textAnimator.setInterpolator(new LinearOutSlowInInterpolator());
219        textAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
220            @Override
221            public void onAnimationUpdate(ValueAnimator valueAnimator) {
222                float animatedValue = (float) valueAnimator.getAnimatedValue();
223                mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, animatedValue);
224            }
225        });
226
227        // Reduce or increase the padding top of the tab.
228        ValueAnimator paddingTopAnimator = ValueAnimator.ofInt(currentTopPadding, finalTopPadding);
229        paddingTopAnimator.setDuration(ACTIVE_ANIMATION_DURATION_MS);
230        paddingTopAnimator.setInterpolator(new LinearOutSlowInInterpolator());
231        paddingTopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
232            @Override
233            public void onAnimationUpdate(ValueAnimator valueAnimator) {
234                int animatedValue = (int) valueAnimator.getAnimatedValue();
235                setPadding(mHorizontalPadding, animatedValue,
236                        mHorizontalPadding, mBottomPadding);
237            }
238        });
239
240        AnimatorSet set = new AnimatorSet();
241        set.playTogether(textAnimator, paddingTopAnimator);
242        return set;
243    }
244}
245