Skip to content

Commit

Permalink
[NavigationBar] Add new itemIconGravity attribute
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 638866190
  • Loading branch information
imhappi authored and pekingme committed Jun 5, 2024
1 parent 4acd570 commit abb6e91
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 210 deletions.
1 change: 1 addition & 0 deletions docs/components/BottomNavigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ The following is an anatomy diagram for the bottom navigation bar:
**Size** | `app:itemIconSize` | `setItemIconSize`<br/>`setItemIconSizeRes`<br/>`getItemIconSize` | `24dp`
**Color (inactive)** | `app:itemIconTint` | `setItemIconTintList`<br/>`getItemIconTintList` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
**Color (active)** | " | " | `?attr/colorOnSecondaryContainer` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/bottomnavigation/res/color/m3_navigation_bar_item_with_indicator_icon_tint.xml))
**Gravity** | `app:itemIconGravity` | `setItemIconGravity`<br/>`getItemIconGravity` | `TOP`

#### Text label attributes

Expand Down
1 change: 1 addition & 0 deletions docs/components/NavigationRail.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ for more attributes.
**Size** | `app:itemIconSize` | `setItemIconSize`<br/>`setItemIconSizeRes`<br/>`getItemIconSize` | `24dp`
**Color (inactive)** | `app:itemIconTint` | `setItemIconTintList`<br/>`getItemIconTintList` | `?attr/colorOnSurfaceVariant`
**Color (active)** | `app:itemIconTint` | `setItemIconTintList`<br/>`getItemIconTintList` | `?attr/colorOnSecondaryContainer`
**Gravity** | `app:itemIconGravity` | `setItemIconGravity`<br/>`getItemIconGravity` | `TOP`

#### Text label attributes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.google.android.material.R;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static java.lang.Math.max;
import static java.lang.Math.min;

import android.content.Context;
import android.content.res.Resources;
Expand All @@ -31,6 +33,7 @@
import androidx.annotation.RestrictTo;
import com.google.android.material.navigation.NavigationBarItemView;
import com.google.android.material.navigation.NavigationBarMenuView;
import com.google.android.material.navigation.NavigationBarView;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -76,76 +79,101 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int totalCount = getChildCount();
tempChildWidths.clear();

int totalWidth = 0;
int maxHeight = 0;

int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
final int heightSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST);

if (isShifting(getLabelVisibilityMode(), visibleCount)
&& isItemHorizontalTranslationEnabled()) {
final View activeChild = getChildAt(getSelectedItemPosition());
int activeItemWidth = activeItemMinWidth;
if (activeChild.getVisibility() != View.GONE) {
// Do an AT_MOST measure pass on the active child to get its desired width, and resize the
// active child view based on that width
activeChild.measure(
MeasureSpec.makeMeasureSpec(activeItemMaxWidth, MeasureSpec.AT_MOST), heightSpec);
activeItemWidth = Math.max(activeItemWidth, activeChild.getMeasuredWidth());
if (getItemIconGravity() == NavigationBarView.ITEM_ICON_GRAVITY_TOP) {
if (isShifting(getLabelVisibilityMode(), visibleCount)
&& isItemHorizontalTranslationEnabled()) {
final View activeChild = getChildAt(getSelectedItemPosition());
int activeItemWidth = activeItemMinWidth;
if (activeChild.getVisibility() != View.GONE) {
// Do an AT_MOST measure pass on the active child to get its desired width, and resize the
// active child view based on that width
activeChild.measure(
MeasureSpec.makeMeasureSpec(activeItemMaxWidth, MeasureSpec.AT_MOST), heightSpec);
activeItemWidth = max(activeItemWidth, activeChild.getMeasuredWidth());
}
final int inactiveCount = visibleCount - (activeChild.getVisibility() != View.GONE ? 1 : 0);
final int activeMaxAvailable = width - inactiveCount * inactiveItemMinWidth;
final int activeWidth = min(activeMaxAvailable, min(activeItemWidth, activeItemMaxWidth));
final int inactiveMaxAvailable =
(width - activeWidth) / (inactiveCount == 0 ? 1 : inactiveCount);
final int inactiveWidth = min(inactiveMaxAvailable, inactiveItemMaxWidth);
int extra = width - activeWidth - inactiveWidth * inactiveCount;

for (int i = 0; i < totalCount; i++) {
int tempChildWidth = 0;
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidth = (i == getSelectedItemPosition()) ? activeWidth : inactiveWidth;
// Account for integer division which sometimes leaves some extra pixel spaces.
// e.g. If the nav was 10px wide, and 3 children were measured to be 3px-3px-3px, there
// would be a 1px gap somewhere, which this fills in.
if (extra > 0) {
tempChildWidth++;
extra--;
}
}
tempChildWidths.add(tempChildWidth);
}
} else {
final int maxAvailable = width / (visibleCount == 0 ? 1 : visibleCount);
final int childWidth = min(maxAvailable, activeItemMaxWidth);
int extra = width - childWidth * visibleCount;
for (int i = 0; i < totalCount; i++) {
int tempChildWidth = 0;
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidth = childWidth;
if (extra > 0) {
tempChildWidth++;
extra--;
}
}
tempChildWidths.add(tempChildWidth);
}
}
final int inactiveCount = visibleCount - (activeChild.getVisibility() != View.GONE ? 1 : 0);
final int activeMaxAvailable = width - inactiveCount * inactiveItemMinWidth;
final int activeWidth =
Math.min(activeMaxAvailable, Math.min(activeItemWidth, activeItemMaxWidth));
final int inactiveMaxAvailable =
(width - activeWidth) / (inactiveCount == 0 ? 1 : inactiveCount);
final int inactiveWidth = Math.min(inactiveMaxAvailable, inactiveItemMaxWidth);
int extra = width - activeWidth - inactiveWidth * inactiveCount;

for (int i = 0; i < totalCount; i++) {
int tempChildWidth = 0;
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidth = (i == getSelectedItemPosition()) ? activeWidth : inactiveWidth;
// Account for integer division which sometimes leaves some extra pixel spaces.
// e.g. If the nav was 10px wide, and 3 children were measured to be 3px-3px-3px, there
// would be a 1px gap somewhere, which this fills in.
if (extra > 0) {
tempChildWidth++;
extra--;
}
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
tempChildWidths.add(tempChildWidth);
child.measure(
MeasureSpec.makeMeasureSpec(tempChildWidths.get(i), MeasureSpec.EXACTLY), heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
totalWidth += child.getMeasuredWidth();
maxHeight = max(maxHeight, child.getMeasuredHeight());
}
} else {
final int maxAvailable = width / (visibleCount == 0 ? 1 : visibleCount);
final int childWidth = Math.min(maxAvailable, activeItemMaxWidth);
int extra = width - childWidth * visibleCount;
} else { // icon gravity is start
int childCount = visibleCount == 0 ? 1 : visibleCount;
// Calculate the min nav item width based on the item count and bar width according to
// these rules:
// 3 items: the items should occupy 60% of the bar's width
// 4 items: the items should occupy 70% of the bar's width
// 5 items: the items should occupy 80% of the bar's width
// 6+ items: the items should occupy 90% of the bar's width
int minChildWidth = Math.round((min((childCount + 3) / 10f, 0.9f) * width) / childCount);
int maxChildWidth = Math.round((float) width / childCount);
for (int i = 0; i < totalCount; i++) {
int tempChildWidth = 0;
if (getChildAt(i).getVisibility() != View.GONE) {
tempChildWidth = childWidth;
if (extra > 0) {
tempChildWidth++;
extra--;
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
child.measure(
MeasureSpec.makeMeasureSpec(maxChildWidth, MeasureSpec.AT_MOST), heightSpec);
if (child.getMeasuredWidth() < minChildWidth) {
child.measure(
MeasureSpec.makeMeasureSpec(minChildWidth, MeasureSpec.EXACTLY), heightSpec);
}
totalWidth += child.getMeasuredWidth();
maxHeight = max(maxHeight, child.getMeasuredHeight());
}
}
tempChildWidths.add(tempChildWidth);
}
}

int totalWidth = 0;
int maxHeight = 0;
for (int i = 0; i < totalCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(
MeasureSpec.makeMeasureSpec(tempChildWidths.get(i), MeasureSpec.EXACTLY), heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
totalWidth += child.getMeasuredWidth();
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}

setMeasuredDimension(totalWidth, Math.max(maxHeight, getSuggestedMinimumHeight()));
setMeasuredDimension(totalWidth, max(maxHeight, getSuggestedMinimumHeight()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,45 @@
~ limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout
android:id="@id/navigation_bar_item_content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="@dimen/design_bottom_navigation_margin"
android:clipChildren="false"
android:duplicateParentState="true"
android:gravity="center"
android:layout_marginTop="@dimen/design_bottom_navigation_margin"
android:orientation="vertical">
<FrameLayout
android:id="@id/navigation_bar_item_icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:layout_gravity="center"
android:duplicateParentState="true">
<View
android:id="@id/navigation_bar_item_active_indicator_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center" />
<ImageView
android:id="@id/navigation_bar_item_icon_view"
android:layout_width="@dimen/design_bottom_navigation_icon_size"
android:layout_height="@dimen/design_bottom_navigation_icon_size"
<LinearLayout
android:id="@id/navigation_bar_item_inner_content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@null"
android:duplicateParentState="true" />
android:clipChildren="false"
android:duplicateParentState="true"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@id/navigation_bar_item_icon_view"
android:layout_width="@dimen/design_bottom_navigation_icon_size"
android:layout_height="@dimen/design_bottom_navigation_icon_size"
android:layout_gravity="center"
android:contentDescription="@null"
android:duplicateParentState="true" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.internal.BaselineLayout
android:id="@id/navigation_bar_item_labels_group"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<item name="activeIndicatorLabelPadding">1dp</item>
<item name="itemActiveIndicatorStyle">@null</item>
<item name="android:minHeight">@dimen/design_bottom_navigation_height</item>
<item name="itemIconGravity">top</item>
</style>

<!-- Default style for BottomNavigationViews.
Expand Down
Loading

0 comments on commit abb6e91

Please sign in to comment.