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