Adding shifting mode to BottomNavigationView.
This change adds a new mode to BottomNavigationView that
follow the https://material.google.com/components/bottom-navigation.html
spec.
Bug: 27675079
Change-Id: I868bc3c8cedd39c4e5a66c3c0ffbadff93bdf329
diff --git a/design/Android.mk b/design/Android.mk
index e9ba5b6..38ca592 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -101,7 +101,8 @@
android-support-design-res \
android-support-v4 \
android-support-v7-appcompat \
- android-support-v7-recyclerview
+ android-support-v7-recyclerview \
+ android-support-transition
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -115,7 +116,8 @@
android-support-design-res \
android-support-v4 \
android-support-v7-appcompat \
- android-support-v7-recyclerview
+ android-support-v7-recyclerview \
+ android-support-transition
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/AndroidManifest.xml b/design/AndroidManifest.xml
index 82f19db..d51186dde 100644
--- a/design/AndroidManifest.xml
+++ b/design/AndroidManifest.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="android.support.design">
- <uses-sdk android:minSdkVersion="9"/>
+ <uses-sdk android:minSdkVersion="9"
+ tools:overrideLibrary="android.support.transition"/>
<application />
</manifest>
diff --git a/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java b/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
new file mode 100644
index 0000000..22501c1
--- /dev/null
+++ b/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.internal;
+
+import android.view.ViewGroup;
+
+class BottomNavigationAnimationHelperBase {
+ void beginDelayedTransition(ViewGroup view) {
+ // Do nothing.
+ }
+}
diff --git a/design/build.gradle b/design/build.gradle
index 4407ba9..8ae45d7 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -6,6 +6,7 @@
compile project(':support-v4')
compile project(':support-appcompat-v7')
compile project(':support-recyclerview-v7')
+ compile project(':support-transition')
androidTestCompile ("com.android.support.test:runner:${project.rootProject.ext.testRunnerVersion}") {
exclude module: 'support-annotations'
diff --git a/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java b/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
new file mode 100644
index 0000000..6681d0b
--- /dev/null
+++ b/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.internal;
+
+import android.support.transition.AutoTransition;
+import android.support.transition.TransitionManager;
+import android.support.transition.TransitionSet;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.view.ViewGroup;
+
+class BottomNavigationAnimationHelperIcs extends BottomNavigationAnimationHelperBase {
+ private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
+
+ private final TransitionSet mSet;
+
+ BottomNavigationAnimationHelperIcs() {
+ mSet = new AutoTransition();
+ mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
+ mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
+ mSet.setInterpolator(new FastOutSlowInInterpolator());
+ TextScale textScale = new TextScale();
+ mSet.addTransition(textScale);
+ }
+
+ void beginDelayedTransition(ViewGroup view) {
+ TransitionManager.beginDelayedTransition(view, mSet);
+ }
+}
diff --git a/design/ics/android/support/design/internal/TextScale.java b/design/ics/android/support/design/internal/TextScale.java
new file mode 100644
index 0000000..219353e
--- /dev/null
+++ b/design/ics/android/support/design/internal/TextScale.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.internal;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.support.transition.Transition;
+import android.support.transition.TransitionValues;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class TextScale extends Transition {
+ private static final String PROPNAME_SCALE = "android:textscale:scale";
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues transitionValues) {
+ if (transitionValues.view instanceof TextView) {
+ TextView textview = (TextView) transitionValues.view;
+ transitionValues.values.put(PROPNAME_SCALE, textview.getScaleX());
+ }
+ }
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(startValues.view instanceof TextView)
+ || !(endValues.view instanceof TextView)) {
+ return null;
+ }
+ final TextView view = (TextView) endValues.view;
+ Map<String, Object> startVals = startValues.values;
+ Map<String, Object> endVals = endValues.values;
+ final float startSize = startVals.get(PROPNAME_SCALE) != null ? (float) startVals.get(
+ PROPNAME_SCALE) : 1f;
+ final float endSize = endVals.get(PROPNAME_SCALE) != null ? (float) endVals.get(
+ PROPNAME_SCALE) : 1f;
+ if (startSize == endSize) {
+ return null;
+ }
+
+ ValueAnimator animator = ValueAnimator.ofFloat(startSize, endSize);
+
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float animatedValue = (float) valueAnimator.getAnimatedValue();
+ view.setScaleX(animatedValue);
+ view.setScaleY(animatedValue);
+ }
+ });
+ return animator;
+ }
+}
diff --git a/design/res/layout/design_bottom_navigation_item.xml b/design/res/layout/design_bottom_navigation_item.xml
index 5aa1042..cc7bb5f 100644
--- a/design/res/layout/design_bottom_navigation_item.xml
+++ b/design/res/layout/design_bottom_navigation_item.xml
@@ -21,6 +21,7 @@
android:layout_height="24dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/design_bottom_navigation_margin"
+ android:layout_marginBottom="@dimen/design_bottom_navigation_margin"
android:duplicateParentState="true" />
<android.support.design.internal.BaselineLayout
android:layout_width="wrap_content"
diff --git a/design/res/values/dimens.xml b/design/res/values/dimens.xml
index 546d137..63e98b8 100644
--- a/design/res/values/dimens.xml
+++ b/design/res/values/dimens.xml
@@ -62,6 +62,7 @@
<dimen name="design_bottom_navigation_text_size">12sp</dimen>
<dimen name="design_bottom_navigation_active_text_size">14sp</dimen>
<dimen name="design_bottom_navigation_margin">8dp</dimen>
+ <dimen name="design_bottom_navigation_item_min_width">56dp</dimen>
<dimen name="design_bottom_navigation_item_max_width">96dp</dimen>
<dimen name="design_bottom_navigation_active_item_max_width">168dp</dimen>
diff --git a/design/src/android/support/design/internal/BottomNavigationItemView.java b/design/src/android/support/design/internal/BottomNavigationItemView.java
index 02d7186..d5e8176 100644
--- a/design/src/android/support/design/internal/BottomNavigationItemView.java
+++ b/design/src/android/support/design/internal/BottomNavigationItemView.java
@@ -16,22 +16,20 @@
package android.support.design.internal;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.NonNull;
import android.support.design.R;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.support.v7.view.menu.MenuItemImpl;
import android.support.v7.view.menu.MenuView;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -43,13 +41,13 @@
public static final int INVALID_ITEM_POSITION = -1;
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
- private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
- private final float mShiftAmount;
+ private final int mDefaultMargin;
+ private final int mShiftAmount;
private final float mScaleUpFactor;
private final float mScaleDownFactor;
- private final float mInactiveLabelSize;
- private final float mActiveLabelSize;
+
+ private boolean mShiftingMode;
private ImageView mIcon;
private final TextView mSmallLabel;
@@ -70,13 +68,15 @@
public BottomNavigationItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mInactiveLabelSize =
- getResources().getDimension(R.dimen.design_bottom_navigation_text_size);
- mActiveLabelSize =
- getResources().getDimension(R.dimen.design_bottom_navigation_active_text_size);
- mShiftAmount = mInactiveLabelSize - mActiveLabelSize;
- mScaleUpFactor = mActiveLabelSize / mInactiveLabelSize;
- mScaleDownFactor = mInactiveLabelSize / mActiveLabelSize;
+ final Resources res = getResources();
+ int inactiveLabelSize =
+ res.getDimensionPixelSize(R.dimen.design_bottom_navigation_text_size);
+ int activeLabelSize = res.getDimensionPixelSize(
+ R.dimen.design_bottom_navigation_active_text_size);
+ mDefaultMargin = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_margin);
+ mShiftAmount = inactiveLabelSize - activeLabelSize;
+ mScaleUpFactor = 1f * activeLabelSize / inactiveLabelSize;
+ mScaleDownFactor = 1f * inactiveLabelSize / activeLabelSize;
LayoutInflater.from(context).inflate(R.layout.design_bottom_navigation_item, this, true);
setBackgroundResource(R.drawable.design_bottom_navigation_item_background);
@@ -90,7 +90,7 @@
public void initialize(MenuItemImpl itemData, int menuType) {
mItemData = itemData;
setCheckable(itemData.isCheckable());
- setChecked(itemData.isChecked(), false);
+ setChecked(itemData.isChecked());
setEnabled(itemData.isEnabled());
setIcon(itemData.getIcon());
setTitle(itemData.getTitle());
@@ -105,6 +105,10 @@
return mItemPosition;
}
+ public void setShiftingMode(boolean enabled) {
+ mShiftingMode = enabled;
+ }
+
@Override
public MenuItemImpl getItemData() {
return mItemData;
@@ -123,24 +127,56 @@
@Override
public void setChecked(boolean checked) {
- setChecked(checked, true);
- }
-
- public void setChecked(boolean checked, boolean animate) {
mItemData.setChecked(checked);
- if (checked) {
- mLargeLabel.setVisibility(VISIBLE);
+ ViewCompat.setPivotX(mLargeLabel, mLargeLabel.getWidth() / 2);
+ ViewCompat.setPivotY(mLargeLabel, mLargeLabel.getBaseline());
+ ViewCompat.setPivotX(mSmallLabel, mSmallLabel.getWidth() / 2);
+ ViewCompat.setPivotY(mSmallLabel, mSmallLabel.getBaseline());
+ if (mShiftingMode) {
+ if (checked) {
+ LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
+ iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ iconParams.topMargin = mDefaultMargin;
+ mIcon.setLayoutParams(iconParams);
+ mLargeLabel.setVisibility(VISIBLE);
+ ViewCompat.setScaleX(mLargeLabel, 1f);
+ ViewCompat.setScaleY(mLargeLabel, 1f);
+ } else {
+ LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
+ iconParams.gravity = Gravity.CENTER;
+ iconParams.topMargin = mDefaultMargin;
+ mIcon.setLayoutParams(iconParams);
+ mLargeLabel.setVisibility(INVISIBLE);
+ ViewCompat.setScaleX(mLargeLabel, 0.5f);
+ ViewCompat.setScaleY(mLargeLabel, 0.5f);
+ }
mSmallLabel.setVisibility(INVISIBLE);
} else {
- mLargeLabel.setVisibility(INVISIBLE);
- mSmallLabel.setVisibility(VISIBLE);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
- if (animate) {
- animate(checked);
+ if (checked) {
+ LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
+ iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ iconParams.topMargin = mDefaultMargin + mShiftAmount;
+ mIcon.setLayoutParams(iconParams);
+ mLargeLabel.setVisibility(VISIBLE);
+ mSmallLabel.setVisibility(INVISIBLE);
+
+ ViewCompat.setScaleX(mLargeLabel, 1f);
+ ViewCompat.setScaleY(mLargeLabel, 1f);
+ ViewCompat.setScaleX(mSmallLabel, mScaleUpFactor);
+ ViewCompat.setScaleY(mSmallLabel, mScaleUpFactor);
} else {
- mIcon.setTranslationY(checked ? mShiftAmount : 0f);
+ LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
+ iconParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ iconParams.topMargin = mDefaultMargin;
+ mIcon.setLayoutParams(iconParams);
+ mLargeLabel.setVisibility(INVISIBLE);
+ mSmallLabel.setVisibility(VISIBLE);
+
+ ViewCompat.setScaleX(mLargeLabel, mScaleDownFactor);
+ ViewCompat.setScaleY(mLargeLabel, mScaleDownFactor);
+ ViewCompat.setScaleX(mSmallLabel, 1f);
+ ViewCompat.setScaleY(mSmallLabel, 1f);
}
}
@@ -206,41 +242,4 @@
? null : ContextCompat.getDrawable(getContext(), background);
ViewCompat.setBackground(this, backgroundDrawable);
}
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
- private void animate(final boolean active) {
- // Grow or shrink the text of the tab.
- final ViewPropertyAnimator textAnimator;
- if (active) {
- mLargeLabel.setVisibility(VISIBLE);
- mSmallLabel.setVisibility(INVISIBLE);
- textAnimator = scaleLabel(mLargeLabel, active);
- } else {
- mLargeLabel.setVisibility(INVISIBLE);
- mSmallLabel.setVisibility(VISIBLE);
- textAnimator = scaleLabel(mSmallLabel, active);
- }
-
- final ViewPropertyAnimator translationAnimation = mIcon.animate()
- .setDuration(ACTIVE_ANIMATION_DURATION_MS)
- .setInterpolator(new LinearOutSlowInInterpolator())
- .translationY(active ? mShiftAmount : 0);
-
- textAnimator.start();
- translationAnimation.start();
- }
-
- private ViewPropertyAnimator scaleLabel(TextView label, boolean active) {
- final float startingTextScale = active ? mScaleDownFactor : mScaleUpFactor;
- label.setPivotX(label.getWidth() / 2);
- label.setPivotY(label.getBaseline());
- label.setScaleX(startingTextScale);
- label.setScaleY(startingTextScale);
- ViewPropertyAnimator textAnimator = label.animate()
- .setDuration(ACTIVE_ANIMATION_DURATION_MS)
- .setInterpolator(new LinearOutSlowInInterpolator())
- .scaleX(1f)
- .scaleY(1f);
- return textAnimator;
- }
}
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index 7080d2e..ec1b542 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -18,28 +18,35 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.os.Build;
import android.support.annotation.Nullable;
import android.support.design.R;
import android.support.v4.util.Pools;
+import android.support.v4.view.ViewCompat;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.view.menu.MenuItemImpl;
import android.support.v7.view.menu.MenuView;
import android.util.AttributeSet;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
/**
* @hide
*/
-public class BottomNavigationMenuView extends LinearLayout implements MenuView {
+public class BottomNavigationMenuView extends ViewGroup implements MenuView {
+
private final int mInactiveItemMaxWidth;
+ private final int mInactiveItemMinWidth;
private final int mActiveItemMaxWidth;
+ private final int mItemHeight;
private final OnClickListener mOnClickListener;
+ private final BottomNavigationAnimationHelperBase mAnimationHelper;
private static final Pools.Pool<BottomNavigationItemView> sItemPool =
new Pools.SynchronizedPool<>(5);
+ private boolean mShiftingMode = true;
+
private BottomNavigationItemView[] mButtons;
private int mActiveButton = 0;
private ColorStateList mItemIconTint;
@@ -55,13 +62,20 @@
public BottomNavigationMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
- setGravity(Gravity.CENTER);
- setOrientation(HORIZONTAL);
-
- mInactiveItemMaxWidth = getResources().getDimensionPixelSize(
+ final Resources res = getResources();
+ mInactiveItemMaxWidth = res.getDimensionPixelSize(
R.dimen.design_bottom_navigation_item_max_width);
- mActiveItemMaxWidth = getResources()
- .getDimensionPixelSize(R.dimen.design_bottom_navigation_active_item_max_width);
+ mInactiveItemMinWidth = res.getDimensionPixelSize(
+ R.dimen.design_bottom_navigation_item_min_width);
+ mActiveItemMaxWidth = res.getDimensionPixelSize(
+ R.dimen.design_bottom_navigation_active_item_max_width);
+ mItemHeight = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_height);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ mAnimationHelper = new BottomNavigationAnimationHelperIcs();
+ } else {
+ mAnimationHelper = new BottomNavigationAnimationHelperBase();
+ }
mOnClickListener = new OnClickListener() {
@Override
@@ -84,6 +98,81 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ final int count = getChildCount();
+
+ final int childState = 0;
+ final int heightSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);
+
+ final int[] childWidths = new int[count];
+ if (mShiftingMode) {
+ final int inactiveCount = count - 1;
+ final int activeMaxAvailable = width - inactiveCount * mInactiveItemMinWidth;
+ final int activeWidth = Math.min(activeMaxAvailable, mActiveItemMaxWidth);
+ final int inactiveMaxAvailable = (width - activeWidth) / inactiveCount;
+ final int inactiveWidth = Math.min(inactiveMaxAvailable, mInactiveItemMaxWidth);
+ int extra = width - activeWidth - inactiveWidth * inactiveCount;
+ for (int i = 0; i < count; i++) {
+ childWidths[i] = (i == mActiveButton) ? activeWidth : inactiveWidth;
+ if (extra > 0) {
+ childWidths[i]++;
+ extra--;
+ }
+ }
+ } else {
+ final int maxAvailable = width / count;
+ final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
+ int extra = width - childWidth * count;
+ for (int i = 0; i < count; i++) {
+ childWidths[i] = childWidth;
+ if (extra > 0) {
+ childWidths[i]++;
+ extra--;
+ }
+ }
+ }
+
+ int totalWidth = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ child.measure(MeasureSpec.makeMeasureSpec(childWidths[i], MeasureSpec.EXACTLY),
+ heightSpec);
+ ViewGroup.LayoutParams params = child.getLayoutParams();
+ params.width = child.getMeasuredWidth();
+ totalWidth += child.getMeasuredWidth();
+ }
+ setMeasuredDimension(
+ ViewCompat.resolveSizeAndState(totalWidth,
+ MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), childState),
+ ViewCompat.resolveSizeAndState(mItemHeight, heightSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int count = getChildCount();
+ final int width = right - left;
+ final int height = bottom - top;
+ int used = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
+ child.layout(width - used - child.getMeasuredWidth(), 0, width - used, height);
+ } else {
+ child.layout(used, 0, child.getMeasuredWidth() + used, height);
+ }
+ used += child.getMeasuredWidth();
+ }
+ }
+
+ @Override
public int getWindowAnimations() {
return 0;
}
@@ -137,6 +226,7 @@
}
removeAllViews();
mButtons = new BottomNavigationItemView[mMenu.size()];
+ mShiftingMode = mMenu.size() > 3;
for (int i = 0; i < mMenu.size(); i++) {
mPresenter.setUpdateSuspended(true);
mMenu.getItem(i).setCheckable(true);
@@ -146,6 +236,7 @@
child.setIconTintList(mItemIconTint);
child.setTextColor(mItemTextColor);
child.setItemBackground(mItemBackgroundRes);
+ child.setShiftingMode(mShiftingMode);
child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
child.setItemPosition(i);
child.setOnClickListener(mOnClickListener);
@@ -170,6 +261,8 @@
private void activateNewButton(int newButton) {
if (mActiveButton == newButton) return;
+ mAnimationHelper.beginDelayedTransition(this);
+
mPresenter.setUpdateSuspended(true);
mButtons[mActiveButton].setChecked(false);
mButtons[newButton].setChecked(true);
@@ -178,28 +271,6 @@
mActiveButton = newButton;
}
- public boolean updateOnSizeChange(int width) {
- if (getChildCount() == 0) {
- return false;
- }
- int available = width / getChildCount();
- int itemWidth = Math.min(available, mActiveItemMaxWidth);
-
- boolean changed = false;
-
- for (int i = 0; i < mButtons.length; i++) {
- ViewGroup.LayoutParams params = mButtons[i].getLayoutParams();
- if (params.width == itemWidth) {
- continue;
- }
- changed = true;
- params.width = itemWidth;
- params.height = ViewGroup.LayoutParams.MATCH_PARENT;
- mButtons[i].setLayoutParams(params);
- }
- return changed;
- }
-
private BottomNavigationItemView getNewItem() {
BottomNavigationItemView item = sItemPool.acquire();
if (item == null) {
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index c5e6ea1..476889f 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
-import android.os.Parcelable;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -32,12 +31,12 @@
import android.support.v7.widget.TintTypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
+import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
/**
* <p>
@@ -96,8 +95,9 @@
mMenu = new BottomNavigationMenu(context);
mMenuView = new BottomNavigationMenuView(context);
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.CENTER;
mMenuView.setLayoutParams(params);
mPresenter.setBottomNavigationMenuView(mMenuView);
@@ -133,7 +133,7 @@
}
a.recycle();
- addView(mMenuView);
+ addView(mMenuView, params);
mMenu.setCallback(new MenuBuilder.Callback() {
@Override
@@ -156,14 +156,6 @@
mListener = listener;
}
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mMenuView.updateOnSizeChange(getMeasuredWidth())) {
- // updateOnSizeChanged has changed LPs, so we need to remeasure
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
/**
* Returns the {@link Menu} instance associated with this bottom navigation bar.
*/
@@ -275,7 +267,7 @@
*
* @return true to display the item as the selected item
*/
- public boolean onNavigationItemSelected(@NonNull MenuItem item);
+ boolean onNavigationItemSelected(@NonNull MenuItem item);
}
private MenuInflater getMenuInflater() {
diff --git a/samples/SupportDesignDemos/Android.mk b/samples/SupportDesignDemos/Android.mk
index de9e302..8ae120e 100644
--- a/samples/SupportDesignDemos/Android.mk
+++ b/samples/SupportDesignDemos/Android.mk
@@ -28,16 +28,19 @@
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-recyclerview \
+ android-support-transition \
android-support-design
LOCAL_RESOURCE_DIR = \
$(LOCAL_PATH)/res \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/recyclerview/res \
+ frameworks/support/transition/res \
frameworks/support/design/res
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.v7.appcompat \
--extra-packages android.support.v7.recyclerview \
+ --extra-packages android.support.transition \
--extra-packages android.support.design \
--no-version-vectors
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
index b21ba3b..83e7314 100644
--- a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
+++ b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout
+<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -30,15 +30,22 @@
android:id="@+id/button_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="50dp"
- android:text="@string/bottomnavigation_add"/>
+ android:text="@string/bottomnavigation_add"
+ android:layout_below="@+id/button_disable"/>
+
+ <Button
+ android:id="@+id/button_remove"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bottomnavigation_remove"
+ android:layout_below="@+id/button_add"/>
<Button
android:id="@+id/button_tint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="100dp"
- android:text="@string/bottomnavigation_tint"/>
+ android:text="@string/bottomnavigation_tint"
+ android:layout_below="@+id/button_remove"/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
@@ -46,6 +53,7 @@
android:layout_height="56dp"
android:layout_gravity="bottom"
android:background="#eee"
+ android:layout_alignParentBottom="true"
app:menu="@menu/sample_bottom_menu"/>
-</FrameLayout>
+</RelativeLayout>
diff --git a/samples/SupportDesignDemos/res/values/strings.xml b/samples/SupportDesignDemos/res/values/strings.xml
index a6a1bc9..95900ca 100644
--- a/samples/SupportDesignDemos/res/values/strings.xml
+++ b/samples/SupportDesignDemos/res/values/strings.xml
@@ -124,6 +124,7 @@
<string name="design_bottom_navigation_view">Bottom navigation view</string>
<string name="bottomnavigation_disable">Disable item</string>
- <string name="bottomnavigation_add">Add item</string>
+ <string name="bottomnavigation_add">Add an item</string>
+ <string name="bottomnavigation_remove">Remove an item</string>
<string name="bottomnavigation_tint">Toggle tint</string>
</resources>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
index fce3c3b..72b50db 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/BottomNavigationViewUsage.java
@@ -50,8 +50,17 @@
buttonAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- MenuItem item = bottom.getMenu().add("Bananas");
- item.setIcon(android.R.drawable.ic_lock_power_off);
+ if (bottom.getMenu().size() < 5) {
+ MenuItem item = bottom.getMenu().add("Bananas");
+ item.setIcon(android.R.drawable.ic_lock_power_off);
+ }
+ }
+ });
+ Button buttonRemove = (Button) findViewById(R.id.button_remove);
+ buttonRemove.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ bottom.getMenu().removeItem(0);
}
});
Button buttonTint = (Button) findViewById(R.id.button_tint);