Skip to content

Commit

Permalink
[BottomNavigationView][NavigationRailView] Modifying NavigationBarVie…
Browse files Browse the repository at this point in the history
…w to match more to specs and restructuring layout to prepare for expressive changes

Changes:
- minHeight for BottomNavigationView is actually a minimum height, not an exact height; the bar height now wraps content properly
- For BottomNavigationView, bottom item padding now counts from the bottom of the label group instead of from the lowest label baseline. The label TextViews also have a min height of the line height supplied by the provided TextAppearance. This aligns with our m3 specs
- Structural changes in navigation item layouts. Previously the label and the icon were not aware of each other in a FrameLayout and could potentially overlap; this is no longer the case as they are in a LinearLayout
- padding between icon and label is 4dp as per specs
- Centering logic changed to center the icon and label as a group, not separately
PiperOrigin-RevId: 635922505
  • Loading branch information
imhappi authored and afohrman committed May 23, 2024
1 parent d85b73f commit 60a525a
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
tempChildWidths.clear();

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

if (isShifting(getLabelVisibilityMode(), visibleCount)
&& isItemHorizontalTranslationEnabled()) {
Expand Down Expand Up @@ -131,6 +131,7 @@ && isItemHorizontalTranslationEnabled()) {
}

int totalWidth = 0;
int maxHeight = 0;
for (int i = 0; i < totalCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
Expand All @@ -141,9 +142,10 @@ && isItemHorizontalTranslationEnabled()) {
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
totalWidth += child.getMeasuredWidth();
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}

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

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

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

import android.content.Context;
import android.os.Build.VERSION;
Expand Down Expand Up @@ -168,6 +167,13 @@ public WindowInsetsCompat onApplyWindowInsets(
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minHeightSpec = makeMinHeightSpec(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, minHeightSpec);
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
setMeasuredDimension(
getMeasuredWidth(),
Math.max(
getMeasuredHeight(),
getSuggestedMinimumHeight() + getPaddingTop() + getPaddingBottom()));
}
}

private int makeMinHeightSpec(int measureSpec) {
Expand All @@ -176,7 +182,7 @@ private int makeMinHeightSpec(int measureSpec) {
minHeight += getPaddingTop() + getPaddingBottom();

return MeasureSpec.makeMeasureSpec(
min(MeasureSpec.getSize(measureSpec), minHeight), MeasureSpec.EXACTLY);
Math.max(MeasureSpec.getSize(measureSpec), minHeight), MeasureSpec.AT_MOST);
}

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

<FrameLayout
android:id="@id/navigation_bar_item_icon_container"
<LinearLayout
android:id="@id/navigation_bar_item_content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_gravity="top|center_horizontal"
android:clipChildren="false"
android:duplicateParentState="true"
android:gravity="center"
android:layout_marginTop="@dimen/design_bottom_navigation_margin"
android:layout_marginBottom="@dimen/design_bottom_navigation_margin"
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="24dp"
android:layout_height="24dp"
android:orientation="vertical">
<FrameLayout
android:id="@id/navigation_bar_item_icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@null"
android:duplicateParentState="true"/>
</FrameLayout>
<com.google.android.material.internal.BaselineLayout
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"
android:layout_gravity="center"
android:contentDescription="@null"
android:duplicateParentState="true" />
</FrameLayout>
<com.google.android.material.internal.BaselineLayout
android:id="@id/navigation_bar_item_labels_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:paddingBottom="@dimen/design_bottom_navigation_label_padding"
android:paddingBottom="@dimen/m3_bottom_nav_item_padding_bottom"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_gravity="center"
android:duplicateParentState="true">
<TextView
<TextView
android:id="@id/navigation_bar_item_small_label_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="end"
android:maxLines="1"
android:textSize="@dimen/design_bottom_navigation_text_size"/>
<TextView
android:includeFontPadding="false"
android:gravity="center_vertical"
android:textSize="@dimen/design_bottom_navigation_text_size" />
<TextView
android:id="@id/navigation_bar_item_large_label_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="end"
android:maxLines="1"
android:gravity="center_vertical"
android:includeFontPadding="false"
android:textSize="@dimen/design_bottom_navigation_active_text_size"
android:visibility="invisible"/>
</com.google.android.material.internal.BaselineLayout>
android:visibility="invisible" />
</com.google.android.material.internal.BaselineLayout>
</LinearLayout>
</merge>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
<item name="itemIconSize">@dimen/design_bottom_navigation_icon_size</item>
<item name="labelVisibilityMode">auto</item>
<item name="itemPaddingTop">@dimen/design_bottom_navigation_margin</item>
<item name="itemPaddingBottom">@dimen/design_bottom_navigation_label_padding</item>
<item name="itemPaddingBottom">0dp</item>
<item name="activeIndicatorLabelPadding">1dp</item>
<item name="itemActiveIndicatorStyle">@null</item>
<item name="android:minHeight">@dimen/design_bottom_navigation_height</item>
</style>
Expand Down Expand Up @@ -80,6 +81,7 @@
<item name="android:minHeight">@dimen/m3_bottom_nav_min_height</item>
<item name="materialThemeOverlay">@style/ThemeOverlay.Material3.BottomNavigationView</item>
<item name="shapeAppearance">@style/ShapeAppearance.M3.Comp.NavigationBar.Container.Shape</item>
<item name="activeIndicatorLabelPadding">@dimen/m3_navigation_item_active_indicator_label_padding</item>
</style>

<style name="Widget.Material3.BottomNavigationView" parent="Base.Widget.Material3.BottomNavigationView">
Expand Down
25 changes: 20 additions & 5 deletions lib/java/com/google/android/material/internal/BaselineLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@
import android.view.ViewGroup;

/**
* A simple ViewGroup that aligns all the views inside on a baseline. Note: bottom padding for this
* view will be measured starting from the baseline.
* A simple ViewGroup that aligns all the views inside on a baseline.
*
* <p>If {@link BaselineLayout#measurePaddingFromBaseline} is true, bottom padding for this view
* will be measured starting from the baseline.
*
* @hide
*/
public class BaselineLayout extends ViewGroup {
private int baseline = -1;

private boolean measurePaddingFromBaseline;

public BaselineLayout(Context context) {
super(context, null, 0);
}
Expand All @@ -42,13 +46,19 @@ public BaselineLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

// TODO(b/338647654): We can remove this once navigation rail is updated to current specs
public void setMeasurePaddingFromBaseline(boolean measurePaddingFromBaseline) {
this.measurePaddingFromBaseline = measurePaddingFromBaseline;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxWidth = 0;
int maxHeight = 0;
int maxChildBaseline = -1;
int maxChildDescent = -1;
int maxMeasuredHeight = 0;
int childState = 0;

for (int i = 0; i < count; i++) {
Expand All @@ -58,6 +68,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}

measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxMeasuredHeight = Math.max(maxMeasuredHeight, child.getMeasuredHeight());
final int baseline = child.getBaseline();
if (baseline != -1) {
maxChildBaseline = Math.max(maxChildBaseline, baseline);
Expand All @@ -68,11 +79,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
childState = View.combineMeasuredStates(childState, child.getMeasuredState());
}
if (maxChildBaseline != -1) {
maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
if (measurePaddingFromBaseline) {
maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
}
this.baseline = maxChildBaseline;
}
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxHeight = Math.max(
measurePaddingFromBaseline ? maxHeight : maxMeasuredHeight + getPaddingBottom(),
getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(
View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
Expand Down
Loading

0 comments on commit 60a525a

Please sign in to comment.