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