Merge changes Id091acf5,Ie27b07ae,I701a2582,I32122d77 into nyc-mr1-dev
* changes:
Start using return value of OnNavigationItemSelectedListener.
Clean up BottomBar docs
Force BottomNavigationView item labels to be singleLine.
Clean-up BottomNavigationMenuView#onMeasure and other style fixes.
diff --git a/design/res/layout/design_bottom_navigation_item.xml b/design/res/layout/design_bottom_navigation_item.xml
index cc7bb5f..67df838 100644
--- a/design/res/layout/design_bottom_navigation_item.xml
+++ b/design/res/layout/design_bottom_navigation_item.xml
@@ -34,6 +34,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/design_bottom_navigation_text_size"
+ android:singleLine="true"
android:duplicateParentState="true" />
<TextView
android:id="@+id/largeLabel"
@@ -41,6 +42,7 @@
android:layout_height="wrap_content"
android:visibility="invisible"
android:textSize="@dimen/design_bottom_navigation_active_text_size"
+ android:singleLine="true"
android:duplicateParentState="true" />
</android.support.design.internal.BaselineLayout>
</merge>
\ No newline at end of file
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index 158dda7..096bdd8 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -16,6 +16,8 @@
package android.support.design.internal;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -32,8 +34,6 @@
import android.view.View;
import android.view.ViewGroup;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* @hide For internal use only.
*/
@@ -55,6 +55,7 @@
private ColorStateList mItemIconTint;
private ColorStateList mItemTextColor;
private int mItemBackgroundRes;
+ private int[] mTempChildWidths;
private BottomNavigationPresenter mPresenter;
private MenuBuilder mMenu;
@@ -85,10 +86,12 @@
public void onClick(View v) {
final BottomNavigationItemView itemView = (BottomNavigationItemView) v;
final int itemPosition = itemView.getItemPosition();
- activateNewButton(itemPosition);
- mMenu.performItemAction(itemView.getItemData(), mPresenter, 0);
+ if (!mMenu.performItemAction(itemView.getItemData(), mPresenter, 0)) {
+ activateNewButton(itemPosition);
+ }
}
};
+ mTempChildWidths = new int[BottomNavigationMenu.MAX_ITEM_COUNT];
}
@Override
@@ -105,10 +108,8 @@
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;
@@ -117,9 +118,9 @@
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;
+ mTempChildWidths[i] = (i == mActiveButton) ? activeWidth : inactiveWidth;
if (extra > 0) {
- childWidths[i]++;
+ mTempChildWidths[i]++;
extra--;
}
}
@@ -128,9 +129,9 @@
final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
int extra = width - childWidth * count;
for (int i = 0; i < count; i++) {
- childWidths[i] = childWidth;
+ mTempChildWidths[i] = childWidth;
if (extra > 0) {
- childWidths[i]++;
+ mTempChildWidths[i]++;
extra--;
}
}
@@ -142,7 +143,7 @@
if (child.getVisibility() == GONE) {
continue;
}
- child.measure(MeasureSpec.makeMeasureSpec(childWidths[i], MeasureSpec.EXACTLY),
+ child.measure(MeasureSpec.makeMeasureSpec(mTempChildWidths[i], MeasureSpec.EXACTLY),
heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
@@ -150,9 +151,8 @@
}
setMeasuredDimension(
ViewCompat.resolveSizeAndState(totalWidth,
- MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), childState),
- ViewCompat.resolveSizeAndState(mItemHeight, heightSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
+ MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
+ ViewCompat.resolveSizeAndState(mItemHeight, heightSpec, 0));
}
@Override
@@ -180,19 +180,34 @@
return 0;
}
- public void setIconTintList(ColorStateList color) {
- mItemIconTint = color;
+ /**
+ * Set the tint which is applied to the menu items' icons.
+ *
+ * @param tint the tint to apply.
+ */
+ public void setIconTintList(ColorStateList tint) {
+ mItemIconTint = tint;
if (mButtons == null) return;
for (BottomNavigationItemView item : mButtons) {
- item.setIconTintList(color);
+ item.setIconTintList(tint);
}
}
+ /**
+ * Returns the tint which is applied to menu items' icons.
+ *
+ * @return The ColorStateList that is used to tint menu items' icons.
+ */
@Nullable
public ColorStateList getIconTintList() {
return mItemIconTint;
}
+ /**
+ * Set the text color to be used on menu items.
+ *
+ * @param color the ColorStateList used for menu items' text.
+ */
public void setItemTextColor(ColorStateList color) {
mItemTextColor = color;
if (mButtons == null) return;
@@ -201,10 +216,19 @@
}
}
+ /**
+ * Returns the text color used on menu items.
+ *
+ * @return the ColorStateList used for menu items' text.
+ */
public ColorStateList getItemTextColor() {
return mItemTextColor;
}
+ /**
+ * Sets the resource id to be used for item background.
+ * @param background the resource id of the background.
+ */
public void setItemBackgroundRes(int background) {
mItemBackgroundRes = background;
if (mButtons == null) return;
@@ -213,6 +237,11 @@
}
}
+ /**
+ * Returns the background resource of the menu items.
+ *
+ * @return the resource id of the background.
+ */
public int getItemBackgroundRes() {
return mItemBackgroundRes;
}
diff --git a/design/src/android/support/design/widget/BottomNavigationView.java b/design/src/android/support/design/widget/BottomNavigationView.java
index 476889f..8a8e0bd 100644
--- a/design/src/android/support/design/widget/BottomNavigationView.java
+++ b/design/src/android/support/design/widget/BottomNavigationView.java
@@ -56,14 +56,28 @@
* </p>
*
* <pre>
+ * layout resource file:
* <android.support.design.widget.BottomNavigationView
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:design="http://schema.android.com/apk/res/android.support.design"
* android:id="@+id/navigation"
- * android:layout_width="wrap_content"
- * android:layout_height="match_parent"
+ * android:layout_width="match_parent"
+ * android:layout_height="56dp"
* android:layout_gravity="start"
* design:menu="@menu/my_navigation_items" />
+ *
+ * res/menu/my_navigation_items.xml:
+ * <menu xmlns:android="http://schemas.android.com/apk/res/android">
+ * <item android:id="@+id/action_search"
+ * android:title="@string/menu_search"
+ * android:icon="@drawable/ic_search" />
+ * <item android:id="@+id/action_settings"
+ * android:title="@string/menu_settings"
+ * android:icon="@drawable/ic_add" />
+ * <item android:id="@+id/action_navigation"
+ * android:title="@string/menu_navigation"
+ * android:icon="@drawable/ic_action_navigation_menu" />
+ * </menu>
* </pre>
*/
public class BottomNavigationView extends FrameLayout {
@@ -138,7 +152,7 @@
mMenu.setCallback(new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- return mListener != null && mListener.onNavigationItemSelected(item);
+ return mListener != null && !mListener.onNavigationItemSelected(item);
}
@Override
@@ -210,7 +224,7 @@
}
/**
- * Returns the tint which is applied to menu items' icons.
+ * Returns the text color used on menu items.
*
* @see #setItemTextColor(ColorStateList)
*
@@ -265,7 +279,9 @@
*
* @param item The selected item
*
- * @return true to display the item as the selected item
+ * @return true to display the item as the selected item and false if the item should not
+ * be selected. Consider setting non-selectable items as disabled preemptively to
+ * make them appear non-interactive.
*/
boolean onNavigationItemSelected(@NonNull MenuItem item);
}
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index 98851b4..0ddbc6d 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -27,11 +27,15 @@
import static org.hamcrest.core.AllOf.allOf;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.support.annotation.ColorInt;
@@ -95,22 +99,38 @@
mock(BottomNavigationView.OnNavigationItemSelectedListener.class);
mBottomNavigation.setOnNavigationItemSelectedListener(mockedListener);
- // Click one of our items
+ // Make the listener return true to allow selecting the item.
+ when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(true);
onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
- // And that our listener has been notified of the click
+ // Verify our listener has been notified of the click
verify(mockedListener, times(1)).onNavigationItemSelected(
mBottomNavigation.getMenu().findItem(R.id.destination_profile));
+ // Verify the item is now selected
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
+
+ // Make the listener return false to disallow selecting the item.
+ when(mockedListener.onNavigationItemSelected(any(MenuItem.class))).thenReturn(false);
+ onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+ isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
+ // Verify our listener has been notified of the click
+ verify(mockedListener, times(1)).onNavigationItemSelected(
+ mBottomNavigation.getMenu().findItem(R.id.destination_people));
+ // Verify the previous item is still selected
+ assertFalse(mBottomNavigation.getMenu().findItem(R.id.destination_people).isChecked());
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_profile).isChecked());
// Set null listener to test that the next click is not going to notify the
- // previously set listener
+ // previously set listener and will allow selecting items.
mBottomNavigation.setOnNavigationItemSelectedListener(null);
// Click one of our items
- onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+ onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
isDescendantOfA(withId(R.id.bottom_navigation)), isDisplayed())).perform(click());
// And that our previous listener has not been notified of the click
verifyNoMoreInteractions(mockedListener);
+ // Verify the correct item is now selected.
+ assertTrue(mBottomNavigation.getMenu().findItem(R.id.destination_home).isChecked());
}
@Test
diff --git a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
index 83e7314..c868430 100644
--- a/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
+++ b/samples/SupportDesignDemos/res/layout/design_bottom_navigation_view.xml
@@ -14,46 +14,56 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <Button
- android:id="@+id/button_disable"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/bottomnavigation_disable"/>
+ <ScrollView android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginBottom="56dp">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:id="@+id/button_disable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bottomnavigation_disable"/>
+ <Button
+ android:id="@+id/button_add"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bottomnavigation_add"
+ android:layout_below="@+id/button_disable"/>
- <Button
- android:id="@+id/button_add"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- 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_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:text="@string/bottomnavigation_tint"
+ android:layout_below="@+id/button_remove"/>
- <Button
- android:id="@+id/button_tint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/bottomnavigation_tint"
- android:layout_below="@+id/button_remove"/>
-
+ <TextView
+ android:id="@+id/selected_item"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button_tint"/>
+ </RelativeLayout>
+ </ScrollView>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
android:background="#eee"
- android:layout_alignParentBottom="true"
app:menu="@menu/sample_bottom_menu"/>
-
-</RelativeLayout>
+</FrameLayout>
diff --git a/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
index 4294f80..d6d4761 100644
--- a/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
+++ b/samples/SupportDesignDemos/res/menu/sample_bottom_menu.xml
@@ -20,7 +20,7 @@
<item android:id="@+id/action_settings"
android:title="@string/menu_settings"
android:icon="@drawable/ic_add"/>
- <item android:id="@+id/action_navigation"
+ <item android:id="@+id/action_music"
android:title="@string/tab_text"
android:icon="@drawable/ic_action_navigation_menu"/>
</menu>
\ No newline at end of file
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 72b50db..3442218 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
@@ -18,11 +18,13 @@
import android.content.res.ColorStateList;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
+import android.widget.TextView;
import com.example.android.support.design.R;
@@ -74,5 +76,26 @@
}
}
});
+ final TextView selectedItem = (TextView) findViewById(R.id.selected_item);
+ bottom.setOnNavigationItemSelectedListener(
+ new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_search:
+ selectedItem.setText("Entering searching mode");
+ break;
+ case R.id.action_settings:
+ selectedItem.setText("Entering settings!?!");
+ break;
+ case R.id.action_music:
+ selectedItem.setText("Play some music");
+ break;
+ default:
+ selectedItem.setText("Selected " + item.getTitle());
+ }
+ return true;
+ }
+ });
}
}