Skip to content

Commit 5a8c39a

Browse files
imhappipaulfthomas
authored andcommitted
[NavigationRail][BottomNavigation] Add attributes for scaling label text to font size and for the max lines for the label
PiperOrigin-RevId: 700052136
1 parent 73b577d commit 5a8c39a

File tree

9 files changed

+165
-17
lines changed

9 files changed

+165
-17
lines changed

docs/components/BottomNavigation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ expands to wrap the content of the Bottom Navigation item when the
319319
**Typography (inactive)** | `app:itemTextAppearanceInactive`<br/>`app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive`<br/>`getItemTextAppearanceInactive`<br/>`setHorizontalItemTextAppearanceInactive`<br/>`getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall`
320320
**Typography (active)** | `app:itemTextAppearanceActive`<br/>`app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive`<br/>`getItemTextAppearanceActive`<br/>`setHorizontalItemTextAppearanceActive`<br/>`getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall`
321321
**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
322+
**Max lines** | `app:labelMaxLines` | `setLabelMaxLines`<br/>`getLabelMaxLines` | `1`
323+
**Scale with font size** | `app:scaleLabelWithFontSize` | `setScaleLabelTextWithFont`<br/>`getScaleLabelTextWithFont` | `false`
322324

323325
#### Styles
324326

docs/components/NavigationRail.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,8 @@ expands to wrap the content of the Navigation Rail item when the
422422
**Typography (inactive)** | `app:itemTextAppearanceInactive`<br/>`app:horizontalItemTextAppearanceInactive` | `setItemTextAppearanceInactive`<br/>`getItemTextAppearanceInactive`<br/>`setHorizontalItemTextAppearanceInactive`<br/>`getHorizontalItemTextAppearanceInactive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal
423423
**Typography (active)** | `app:itemTextAppearanceActive`<br/>`app:horizontalItemTextAppearanceActive` | `setItemTextAppearanceActive`<br/>`getItemTextAppearanceActive`<br/>`setHorizontalItemTextAppearanceActive`<br/>`getHorizontalItemTextAppearanceActive` | `?attr/textAppearanceTitleSmall` for regular item configuration, `?attr/textAppearanceLabelLarge` for horizontal
424424
**Typography (active)** | `app:itemTextAppearanceActiveBoldEnabled` | `setItemTextAppearanceActiveBoldEnabled` | `true`
425+
**Max lines** | `app:labelMaxLines` | `setLabelMaxLines`<br/>`getLabelMaxLines` | `1`
426+
**Scale with font size** | `app:scaleLabelWithFontSize` | `setScaleLabelTextWithFont`<br/>`getScaleLabelTextWithFont` | `false`
425427

426428
#### Styles
427429

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
android:layout_height="wrap_content"
7171
android:duplicateParentState="true"
7272
android:ellipsize="end"
73-
android:maxLines="1"
7473
android:includeFontPadding="false"
7574
android:gravity="center_vertical"
7675
android:textSize="@dimen/design_bottom_navigation_text_size" />
@@ -80,7 +79,6 @@
8079
android:layout_height="wrap_content"
8180
android:duplicateParentState="true"
8281
android:ellipsize="end"
83-
android:maxLines="1"
8482
android:gravity="center_vertical"
8583
android:includeFontPadding="false"
8684
android:textSize="@dimen/design_bottom_navigation_active_text_size"

lib/java/com/google/android/material/navigation/NavigationBarItemView.java

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public abstract class NavigationBarItemView extends FrameLayout
166166
private boolean expanded = false;
167167
private boolean onlyShowWhenExpanded = false;
168168
private boolean measurePaddingFromBaseline = false;
169+
private boolean scaleLabelSizeWithFont = false;
169170

170171
public NavigationBarItemView(@NonNull Context context) {
171172
super(context);
@@ -208,20 +209,37 @@ public NavigationBarItemView(@NonNull Context context) {
208209
if (icon.getVisibility() == VISIBLE) {
209210
tryUpdateBadgeBounds(icon);
210211
}
211-
// If item icon gravity is start, we want to update the active indicator width in a layout
212-
// change listener to keep the active indicator size up to date with the content width.
213212
LayoutParams lp = (LayoutParams) innerContentContainer.getLayoutParams();
214213
int newWidth = right - left + lp.rightMargin + lp.leftMargin;
214+
int newHeight = bottom - top + lp.topMargin + lp.bottomMargin;
215+
// If item icon gravity is start, we want to update the active indicator width in a layout
216+
// change listener to keep the active indicator size up to date with the content width.
215217
if (itemIconGravity == ITEM_ICON_GRAVITY_START
216-
&& activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT
217-
&& newWidth != activeIndicatorView.getMeasuredWidth()) {
218+
&& activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT) {
219+
218220
LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams();
219-
int minWidth =
220-
min(
221-
activeIndicatorDesiredWidth,
222-
getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2));
223-
indicatorParams.width = max(newWidth, minWidth);
224-
activeIndicatorView.setLayoutParams(indicatorParams);
221+
boolean layoutParamsChanged = false;
222+
if (activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT
223+
&& activeIndicatorView.getMeasuredWidth() != newWidth) {
224+
int minWidth =
225+
min(
226+
activeIndicatorDesiredWidth,
227+
getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2));
228+
indicatorParams.width = max(newWidth, minWidth);
229+
layoutParamsChanged = true;
230+
}
231+
232+
// We expect the active indicator height to be larger than the height of the
233+
// inner content due to having a min height, but if it is smaller (for example due to
234+
// the text content changing to be multi-line) it should encompass that
235+
if (activeIndicatorView.getMeasuredHeight() < newHeight) {
236+
indicatorParams.height = newHeight;
237+
layoutParamsChanged = true;
238+
}
239+
240+
if (layoutParamsChanged) {
241+
activeIndicatorView.setLayoutParams(indicatorParams);
242+
}
225243
}
226244
});
227245
}
@@ -819,12 +837,28 @@ public void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFromB
819837
requestLayout();
820838
}
821839

840+
public void setLabelFontScalingEnabled(boolean scaleLabelSizeWithFont) {
841+
this.scaleLabelSizeWithFont = scaleLabelSizeWithFont;
842+
setTextAppearanceActive(textAppearanceActive);
843+
setTextAppearanceInactive(textAppearanceInactive);
844+
setHorizontalTextAppearanceActive(horizontalTextAppearanceActive);
845+
setHorizontalTextAppearanceInactive(horizontalTextAppearanceInactive);
846+
}
847+
848+
private void setTextAppearanceForLabel(TextView label, int textAppearance) {
849+
if (scaleLabelSizeWithFont) {
850+
TextViewCompat.setTextAppearance(label, textAppearance);
851+
} else {
852+
setTextAppearanceWithoutFontScaling(label, textAppearance);
853+
}
854+
}
855+
822856
private void updateInactiveLabelTextAppearance(
823857
@Nullable TextView smallLabel, @StyleRes int textAppearanceInactive) {
824858
if (smallLabel == null) {
825859
return;
826860
}
827-
setTextAppearanceWithoutFontScaling(smallLabel, textAppearanceInactive);
861+
setTextAppearanceForLabel(smallLabel, textAppearanceInactive);
828862
calculateTextScaleFactors();
829863
smallLabel.setMinimumHeight(
830864
MaterialResources.getUnscaledLineHeight(
@@ -841,7 +875,7 @@ private void updateActiveLabelTextAppearance(
841875
if (largeLabel == null) {
842876
return;
843877
}
844-
setTextAppearanceWithoutFontScaling(largeLabel, textAppearanceActive);
878+
setTextAppearanceForLabel(largeLabel, textAppearanceActive);
845879
calculateTextScaleFactors();
846880
largeLabel.setMinimumHeight(
847881
MaterialResources.getUnscaledLineHeight(
@@ -911,6 +945,37 @@ private static void setTextAppearanceWithoutFontScaling(
911945
}
912946
}
913947

948+
public void setLabelMaxLines(int labelMaxLines) {
949+
smallLabel.setMaxLines(labelMaxLines);
950+
largeLabel.setMaxLines(labelMaxLines);
951+
expandedSmallLabel.setMaxLines(labelMaxLines);
952+
expandedLargeLabel.setMaxLines(labelMaxLines);
953+
954+
// Due to b/316260445 that was fixed in V+, text with ellipses may be cut off when centered
955+
// due to letter spacing being miscalculated for the ellipses character. We only center the text
956+
// in the following scenarios:
957+
// 1. API level is greater than 34, OR
958+
// 2. The text is not cut off by an ellipses
959+
if (VERSION.SDK_INT > VERSION_CODES.UPSIDE_DOWN_CAKE) {
960+
smallLabel.setGravity(Gravity.CENTER);
961+
largeLabel.setGravity(Gravity.CENTER);
962+
} else if (labelMaxLines > 1) {
963+
// If not single-line, remove the ellipses and center. Removing the ellipses is an unfortunate
964+
// tradeoff due to this bug. We do not want to remove the ellipses for single-line text
965+
// because centering text is not useful (since the textview is centered already) and we
966+
// would rather keep the ellipses.
967+
smallLabel.setEllipsize(null);
968+
largeLabel.setEllipsize(null);
969+
smallLabel.setGravity(Gravity.CENTER);
970+
largeLabel.setGravity(Gravity.CENTER);
971+
} else {
972+
smallLabel.setGravity(Gravity.CENTER_VERTICAL);
973+
largeLabel.setGravity(Gravity.CENTER_VERTICAL);
974+
}
975+
976+
requestLayout();
977+
}
978+
914979
public void setTextColor(@Nullable ColorStateList color) {
915980
textColor = color;
916981
if (color != null) {
@@ -1133,7 +1198,8 @@ private void updateActiveIndicatorLayoutParams(int availableWidth) {
11331198
} else {
11341199
newWidth = min(activeIndicatorExpandedDesiredWidth, adjustedAvailableWidth);
11351200
}
1136-
newHeight = activeIndicatorExpandedDesiredHeight;
1201+
newHeight =
1202+
max(activeIndicatorExpandedDesiredHeight, innerContentContainer.getMeasuredHeight());
11371203
}
11381204
LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams();
11391205
// If the label visibility is unlabeled, make the active indicator's height equal to its

lib/java/com/google/android/material/navigation/NavigationBarMenuView.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ public abstract class NavigationBarMenuView extends ViewGroup implements MenuVie
122122
private NavigationBarPresenter presenter;
123123
private NavigationBarMenuBuilder menu;
124124
private boolean measurePaddingFromLabelBaseline;
125+
private boolean scaleLabelWithFont;
126+
private int labelMaxLines = 1;
125127

126128
private int itemPoolSize = 0;
127129
private boolean expanded;
@@ -502,6 +504,38 @@ public void setMeasurePaddingFromLabelBaseline(boolean measurePaddingFromLabelBa
502504
}
503505
}
504506

507+
public void setLabelFontScalingEnabled(boolean scaleLabelWithFont) {
508+
this.scaleLabelWithFont = scaleLabelWithFont;
509+
if (buttons != null) {
510+
for (NavigationBarMenuItemView item : buttons) {
511+
if (item instanceof NavigationBarItemView) {
512+
((NavigationBarItemView) item)
513+
.setLabelFontScalingEnabled(scaleLabelWithFont);
514+
}
515+
}
516+
}
517+
}
518+
519+
public boolean getScaleLabelTextWithFont() {
520+
return scaleLabelWithFont;
521+
}
522+
523+
public void setLabelMaxLines(int labelMaxLines) {
524+
this.labelMaxLines = labelMaxLines;
525+
if (buttons != null) {
526+
for (NavigationBarMenuItemView item : buttons) {
527+
if (item instanceof NavigationBarItemView) {
528+
((NavigationBarItemView) item)
529+
.setLabelMaxLines(labelMaxLines);
530+
}
531+
}
532+
}
533+
}
534+
535+
public int getLabelMaxLines() {
536+
return labelMaxLines;
537+
}
538+
505539
/**
506540
* Get the distance between the item's active indicator container and the label.
507541
*/
@@ -1062,6 +1096,7 @@ private NavigationBarItemView createMenuItem(
10621096
presenter.setUpdateSuspended(false);
10631097
NavigationBarItemView child = getNewItem();
10641098
child.setShifting(shifting);
1099+
child.setLabelMaxLines(labelMaxLines);
10651100
child.setIconTintList(itemIconTint);
10661101
child.setIconSize(itemIconSize);
10671102
// Set the text color the default, then look for another text color in order of precedence.
@@ -1079,6 +1114,7 @@ private NavigationBarItemView createMenuItem(
10791114
child.setItemPaddingBottom(itemPaddingBottom);
10801115
}
10811116
child.setMeasureBottomPaddingFromLabelBaseline(measurePaddingFromLabelBaseline);
1117+
child.setLabelFontScalingEnabled(scaleLabelWithFont);
10821118
if (itemActiveIndicatorLabelPadding != NO_PADDING) {
10831119
child.setActiveIndicatorLabelPadding(itemActiveIndicatorLabelPadding);
10841120
}

lib/java/com/google/android/material/navigation/NavigationBarView.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ public NavigationBarView(
327327
setMeasureBottomPaddingFromLabelBaseline(attributes.getBoolean(
328328
R.styleable.NavigationBarView_measureBottomPaddingFromLabelBaseline, true));
329329

330+
setLabelFontScalingEnabled(
331+
attributes.getBoolean(R.styleable.NavigationBarView_labelFontScalingEnabled, false));
332+
333+
setLabelMaxLines(
334+
attributes.getInteger(R.styleable.NavigationBarView_labelMaxLines, 1));
335+
330336
int activeIndicatorStyleResId =
331337
attributes.getResourceId(R.styleable.NavigationBarView_itemActiveIndicatorStyle, 0);
332338

@@ -708,6 +714,34 @@ private void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFrom
708714
menuView.setMeasurePaddingFromLabelBaseline(measurePaddingFromBaseline);
709715
}
710716

717+
/**
718+
* Sets whether or not the label text should scale with the system font size.
719+
*/
720+
public void setLabelFontScalingEnabled(boolean labelFontScalingEnabled) {
721+
menuView.setLabelFontScalingEnabled(labelFontScalingEnabled);
722+
}
723+
724+
/**
725+
* Returns whether or not the label text should scale with the system font size.
726+
*/
727+
public boolean getScaleLabelTextWithFont() {
728+
return menuView.getScaleLabelTextWithFont();
729+
}
730+
731+
/**
732+
* Set the max lines limit for the label text.
733+
*/
734+
public void setLabelMaxLines(int labelMaxLines) {
735+
menuView.setLabelMaxLines(labelMaxLines);
736+
}
737+
738+
/**
739+
* Returns the max lines limit for the label text.
740+
*/
741+
public int getLabelMaxLines(int labelMaxLines) {
742+
return menuView.getLabelMaxLines();
743+
}
744+
711745
/**
712746
* Set the distance between the active indicator container and the item's label.
713747
*/

lib/java/com/google/android/material/navigation/res-public/values/public.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,9 @@
6666
<public name="expandedWidth" type="attr"/>
6767
<public name="expandedHeight" type="attr"/>
6868
<public name="expandedMarginHorizontal" type="attr"/>
69+
70+
<public name="scaleLabelWithFontSize" type="attr"/>
71+
<public name="labelMaxLines" type="attr"/>
72+
6973
<public name="ShapeAppearance.Material3.NavigationBarView.ActiveIndicator" type="style"/>
7074
</resources>

lib/java/com/google/android/material/navigation/res/values/attrs.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@
118118
<!-- Measure the item's bottom padding from the baseline of the label, not the bottom of its
119119
view. -->
120120
<attr name="measureBottomPaddingFromLabelBaseline" format="boolean"/>
121+
122+
<!-- Whether or not to scale the label with the font size. -->
123+
<attr name="labelFontScalingEnabled" format="boolean"/>
124+
125+
<!-- The maximum number of lines the label can be. -->
126+
<attr name="labelMaxLines" format="integer"/>
121127
</declare-styleable>
122128

123129
<declare-styleable name="NavigationBarActiveIndicator">

lib/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
android:layout_height="wrap_content"
7373
android:duplicateParentState="true"
7474
android:ellipsize="end"
75-
android:maxLines="1"
75+
android:gravity="center_vertical"
7676
android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal"
7777
android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal"
7878
android:textSize="@dimen/mtrl_navigation_rail_text_size" />
@@ -81,8 +81,8 @@
8181
android:layout_width="wrap_content"
8282
android:layout_height="wrap_content"
8383
android:duplicateParentState="true"
84+
android:gravity="center_vertical"
8485
android:ellipsize="end"
85-
android:maxLines="1"
8686
android:textSize="@dimen/mtrl_navigation_rail_active_text_size"
8787
android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal"
8888
android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal"

0 commit comments

Comments
 (0)