Skip to content

Commit 60a525a

Browse files
imhappiafohrman
authored andcommitted
[BottomNavigationView][NavigationRailView] Modifying NavigationBarView 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
1 parent d85b73f commit 60a525a

File tree

14 files changed

+252
-140
lines changed

14 files changed

+252
-140
lines changed

lib/java/com/google/android/material/bottomnavigation/BottomNavigationMenuView.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
7777
tempChildWidths.clear();
7878

7979
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
80-
final int heightSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY);
80+
final int heightSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST);
8181

8282
if (isShifting(getLabelVisibilityMode(), visibleCount)
8383
&& isItemHorizontalTranslationEnabled()) {
@@ -131,6 +131,7 @@ && isItemHorizontalTranslationEnabled()) {
131131
}
132132

133133
int totalWidth = 0;
134+
int maxHeight = 0;
134135
for (int i = 0; i < totalCount; i++) {
135136
final View child = getChildAt(i);
136137
if (child.getVisibility() == GONE) {
@@ -141,9 +142,10 @@ && isItemHorizontalTranslationEnabled()) {
141142
ViewGroup.LayoutParams params = child.getLayoutParams();
142143
params.width = child.getMeasuredWidth();
143144
totalWidth += child.getMeasuredWidth();
145+
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
144146
}
145147

146-
setMeasuredDimension(totalWidth, parentHeight);
148+
setMeasuredDimension(totalWidth, Math.max(maxHeight, getSuggestedMinimumHeight()));
147149
}
148150

149151
@Override

lib/java/com/google/android/material/bottomnavigation/BottomNavigationView.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.google.android.material.R;
2020

2121
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
22-
import static java.lang.Math.min;
2322

2423
import android.content.Context;
2524
import android.os.Build.VERSION;
@@ -168,6 +167,13 @@ public WindowInsetsCompat onApplyWindowInsets(
168167
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
169168
int minHeightSpec = makeMinHeightSpec(heightMeasureSpec);
170169
super.onMeasure(widthMeasureSpec, minHeightSpec);
170+
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
171+
setMeasuredDimension(
172+
getMeasuredWidth(),
173+
Math.max(
174+
getMeasuredHeight(),
175+
getSuggestedMinimumHeight() + getPaddingTop() + getPaddingBottom()));
176+
}
171177
}
172178

173179
private int makeMinHeightSpec(int measureSpec) {
@@ -176,7 +182,7 @@ private int makeMinHeightSpec(int measureSpec) {
176182
minHeight += getPaddingTop() + getPaddingBottom();
177183

178184
return MeasureSpec.makeMeasureSpec(
179-
min(MeasureSpec.getSize(measureSpec), minHeight), MeasureSpec.EXACTLY);
185+
Math.max(MeasureSpec.getSize(measureSpec), minHeight), MeasureSpec.AT_MOST);
180186
}
181187

182188
return measureSpec;

lib/java/com/google/android/material/bottomnavigation/res/layout/design_bottom_navigation_item.xml

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,65 @@
1515
~ limitations under the License.
1616
-->
1717
<merge xmlns:android="http://schemas.android.com/apk/res/android">
18-
19-
<FrameLayout
20-
android:id="@id/navigation_bar_item_icon_container"
18+
<LinearLayout
19+
android:id="@id/navigation_bar_item_content_container"
2120
android:layout_width="wrap_content"
2221
android:layout_height="wrap_content"
23-
android:layout_gravity="center_horizontal"
22+
android:layout_gravity="top|center_horizontal"
23+
android:clipChildren="false"
24+
android:duplicateParentState="true"
25+
android:gravity="center"
2426
android:layout_marginTop="@dimen/design_bottom_navigation_margin"
25-
android:layout_marginBottom="@dimen/design_bottom_navigation_margin"
26-
android:duplicateParentState="true">
27-
<View
28-
android:id="@id/navigation_bar_item_active_indicator_view"
29-
android:layout_width="0dp"
30-
android:layout_height="0dp"
31-
android:layout_gravity="center" />
32-
<ImageView
33-
android:id="@id/navigation_bar_item_icon_view"
34-
android:layout_width="24dp"
35-
android:layout_height="24dp"
27+
android:orientation="vertical">
28+
<FrameLayout
29+
android:id="@id/navigation_bar_item_icon_container"
30+
android:layout_width="wrap_content"
31+
android:layout_height="wrap_content"
3632
android:layout_gravity="center"
37-
android:contentDescription="@null"
38-
android:duplicateParentState="true"/>
39-
</FrameLayout>
40-
<com.google.android.material.internal.BaselineLayout
33+
android:duplicateParentState="true">
34+
<View
35+
android:id="@id/navigation_bar_item_active_indicator_view"
36+
android:layout_width="0dp"
37+
android:layout_height="0dp"
38+
android:layout_gravity="center" />
39+
<ImageView
40+
android:id="@id/navigation_bar_item_icon_view"
41+
android:layout_width="@dimen/design_bottom_navigation_icon_size"
42+
android:layout_height="@dimen/design_bottom_navigation_icon_size"
43+
android:layout_gravity="center"
44+
android:contentDescription="@null"
45+
android:duplicateParentState="true" />
46+
</FrameLayout>
47+
<com.google.android.material.internal.BaselineLayout
4148
android:id="@id/navigation_bar_item_labels_group"
4249
android:layout_width="wrap_content"
4350
android:layout_height="wrap_content"
44-
android:layout_gravity="bottom|center_horizontal"
45-
android:paddingBottom="@dimen/design_bottom_navigation_label_padding"
51+
android:paddingBottom="@dimen/m3_bottom_nav_item_padding_bottom"
4652
android:clipChildren="false"
4753
android:clipToPadding="false"
54+
android:layout_gravity="center"
4855
android:duplicateParentState="true">
49-
<TextView
56+
<TextView
5057
android:id="@id/navigation_bar_item_small_label_view"
5158
android:layout_width="wrap_content"
5259
android:layout_height="wrap_content"
5360
android:duplicateParentState="true"
5461
android:ellipsize="end"
5562
android:maxLines="1"
56-
android:textSize="@dimen/design_bottom_navigation_text_size"/>
57-
<TextView
63+
android:includeFontPadding="false"
64+
android:gravity="center_vertical"
65+
android:textSize="@dimen/design_bottom_navigation_text_size" />
66+
<TextView
5867
android:id="@id/navigation_bar_item_large_label_view"
5968
android:layout_width="wrap_content"
6069
android:layout_height="wrap_content"
6170
android:duplicateParentState="true"
6271
android:ellipsize="end"
6372
android:maxLines="1"
73+
android:gravity="center_vertical"
74+
android:includeFontPadding="false"
6475
android:textSize="@dimen/design_bottom_navigation_active_text_size"
65-
android:visibility="invisible"/>
66-
</com.google.android.material.internal.BaselineLayout>
76+
android:visibility="invisible" />
77+
</com.google.android.material.internal.BaselineLayout>
78+
</LinearLayout>
6779
</merge>

lib/java/com/google/android/material/bottomnavigation/res/values/styles.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
<item name="itemIconSize">@dimen/design_bottom_navigation_icon_size</item>
2727
<item name="labelVisibilityMode">auto</item>
2828
<item name="itemPaddingTop">@dimen/design_bottom_navigation_margin</item>
29-
<item name="itemPaddingBottom">@dimen/design_bottom_navigation_label_padding</item>
29+
<item name="itemPaddingBottom">0dp</item>
30+
<item name="activeIndicatorLabelPadding">1dp</item>
3031
<item name="itemActiveIndicatorStyle">@null</item>
3132
<item name="android:minHeight">@dimen/design_bottom_navigation_height</item>
3233
</style>
@@ -80,6 +81,7 @@
8081
<item name="android:minHeight">@dimen/m3_bottom_nav_min_height</item>
8182
<item name="materialThemeOverlay">@style/ThemeOverlay.Material3.BottomNavigationView</item>
8283
<item name="shapeAppearance">@style/ShapeAppearance.M3.Comp.NavigationBar.Container.Shape</item>
84+
<item name="activeIndicatorLabelPadding">@dimen/m3_navigation_item_active_indicator_label_padding</item>
8385
</style>
8486

8587
<style name="Widget.Material3.BottomNavigationView" parent="Base.Widget.Material3.BottomNavigationView">

lib/java/com/google/android/material/internal/BaselineLayout.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@
2222
import android.view.ViewGroup;
2323

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

35+
private boolean measurePaddingFromBaseline;
36+
3337
public BaselineLayout(Context context) {
3438
super(context, null, 0);
3539
}
@@ -42,13 +46,19 @@ public BaselineLayout(Context context, AttributeSet attrs, int defStyleAttr) {
4246
super(context, attrs, defStyleAttr);
4347
}
4448

49+
// TODO(b/338647654): We can remove this once navigation rail is updated to current specs
50+
public void setMeasurePaddingFromBaseline(boolean measurePaddingFromBaseline) {
51+
this.measurePaddingFromBaseline = measurePaddingFromBaseline;
52+
}
53+
4554
@Override
4655
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4756
final int count = getChildCount();
4857
int maxWidth = 0;
4958
int maxHeight = 0;
5059
int maxChildBaseline = -1;
5160
int maxChildDescent = -1;
61+
int maxMeasuredHeight = 0;
5262
int childState = 0;
5363

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

6070
measureChild(child, widthMeasureSpec, heightMeasureSpec);
71+
maxMeasuredHeight = Math.max(maxMeasuredHeight, child.getMeasuredHeight());
6172
final int baseline = child.getBaseline();
6273
if (baseline != -1) {
6374
maxChildBaseline = Math.max(maxChildBaseline, baseline);
@@ -68,11 +79,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
6879
childState = View.combineMeasuredStates(childState, child.getMeasuredState());
6980
}
7081
if (maxChildBaseline != -1) {
71-
maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
72-
maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
82+
if (measurePaddingFromBaseline) {
83+
maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
84+
maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
85+
}
7386
this.baseline = maxChildBaseline;
7487
}
75-
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
88+
maxHeight = Math.max(
89+
measurePaddingFromBaseline ? maxHeight : maxMeasuredHeight + getPaddingBottom(),
90+
getSuggestedMinimumHeight());
7691
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
7792
setMeasuredDimension(
7893
View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),

0 commit comments

Comments
 (0)