Skip to content

Commit b36919f

Browse files
imhappidrchen
authored andcommitted
[NavigationRail] Add expanded state
PiperOrigin-RevId: 660106312
1 parent b34fff3 commit b36919f

File tree

6 files changed

+143
-11
lines changed

6 files changed

+143
-11
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<attr name="android:background"/>
4444
<!-- The initial expanded state for the AppBarLayout. This only takes effect when this
4545
view is a direct child of a CoordinatorLayout. -->
46-
<attr name="expanded" format="boolean"/>
46+
<attr name="expanded" />
4747
<attr name="android:keyboardNavigationCluster"/>
4848
<attr name="android:touchscreenBlocksFocus"/>
4949
<!-- Whether the {@link AppBarLayout} should lift on scroll. If set to

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ public NavigationBarItemView(@NonNull Context context) {
188188
// If item icon gravity is start, we want to update the active indicator width in a layout
189189
// change listener to keep the active indicator size up to date with the content width.
190190
if (itemIconGravity == ITEM_ICON_GRAVITY_START
191-
&& activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT) {
191+
&& activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT
192+
&& (right - left) != (oldRight - oldLeft)) {
192193
LayoutParams lp = (LayoutParams) innerContentContainer.getLayoutParams();
193194
int newWidth = right - left + lp.rightMargin + lp.leftMargin;
194195
LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams();

lib/java/com/google/android/material/navigationrail/NavigationRailView.java

Lines changed: 132 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
2222
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
23+
import static java.lang.Math.max;
2324
import static java.lang.Math.min;
2425

2526
import android.content.Context;
@@ -109,11 +110,23 @@ public class NavigationRailView extends NavigationBarView {
109110

110111
private final int contentMarginTop;
111112
private final int headerMarginBottom;
113+
private final int minExpandedWidth;
114+
private final int maxExpandedWidth;
112115
@Nullable private View headerView;
113116
@Nullable private Boolean paddingTopSystemWindowInsets = null;
114117
@Nullable private Boolean paddingBottomSystemWindowInsets = null;
115118
@Nullable private Boolean paddingStartSystemWindowInsets = null;
116119

120+
private boolean expanded = false;
121+
private int collapsedItemSpacing;
122+
private int collapsedItemMinHeight = NO_ITEM_MINIMUM_HEIGHT;
123+
@ItemIconGravity private int collapsedIconGravity = ITEM_ICON_GRAVITY_TOP;
124+
@ItemGravity private int collapsedItemGravity = ITEM_GRAVITY_TOP_CENTER;
125+
private int expandedItemMinHeight;
126+
@ItemIconGravity private int expandedIconGravity;
127+
@ItemGravity private int expandedItemGravity;
128+
private int expandedItemSpacing;
129+
117130
public NavigationRailView(@NonNull Context context) {
118131
this(context, null);
119132
}
@@ -133,6 +146,24 @@ public NavigationRailView(
133146

134147
// Ensure we are using the correctly themed context rather than the context that was passed in.
135148
context = getContext();
149+
minExpandedWidth =
150+
getContext()
151+
.getResources()
152+
.getDimensionPixelSize(R.dimen.m3_navigation_rail_min_expanded_width);
153+
maxExpandedWidth =
154+
getContext()
155+
.getResources()
156+
.getDimensionPixelSize(R.dimen.m3_navigation_rail_max_expanded_width);
157+
expandedItemSpacing =
158+
getContext()
159+
.getResources()
160+
.getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_spacing);
161+
expandedItemMinHeight =
162+
getContext()
163+
.getResources()
164+
.getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_min_height);
165+
expandedItemGravity = ITEM_GRAVITY_START_CENTER;
166+
expandedIconGravity = ITEM_ICON_GRAVITY_START;
136167

137168
/* Custom attributes */
138169
TintTypedArray attributes =
@@ -155,7 +186,7 @@ public NavigationRailView(
155186
attributes.getInt(R.styleable.NavigationRailView_menuGravity, DEFAULT_MENU_GRAVITY));
156187

157188
if (attributes.hasValue(R.styleable.NavigationRailView_itemMinHeight)) {
158-
setItemMinimumHeight(
189+
setCollapsedItemMinimumHeight(
159190
attributes.getDimensionPixelSize(
160191
R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT));
161192
}
@@ -189,14 +220,61 @@ public NavigationRailView(
189220
AnimationUtils.lerp(getItemPaddingBottom(), largeFontBottomPadding, progress);
190221
setItemPaddingTop(Math.round(topPadding));
191222
setItemPaddingBottom(Math.round(bottomPadding));
192-
setItemSpacing(
223+
setCollapsedItemSpacing(
193224
attributes.getDimensionPixelSize(R.styleable.NavigationRailView_itemSpacing, 0));
194225

226+
setExpanded(attributes.getBoolean(R.styleable.NavigationRailView_expanded, false));
227+
195228
attributes.recycle();
196229

197230
applyWindowInsets();
198231
}
199232

233+
@Override
234+
public void setItemIconGravity(int itemIconGravity) {
235+
collapsedIconGravity = itemIconGravity;
236+
expandedIconGravity = itemIconGravity;
237+
super.setItemIconGravity(itemIconGravity);
238+
}
239+
240+
@Override
241+
public int getItemIconGravity() {
242+
return getNavigationRailMenuView().getItemIconGravity();
243+
}
244+
245+
@Override
246+
public void setItemGravity(int itemGravity) {
247+
collapsedItemGravity = itemGravity;
248+
expandedItemGravity = itemGravity;
249+
super.setItemGravity(itemGravity);
250+
}
251+
252+
@Override
253+
public int getItemGravity() {
254+
return getNavigationRailMenuView().getItemGravity();
255+
}
256+
257+
private void setExpanded(boolean expanded) {
258+
if (this.expanded == expanded) {
259+
return;
260+
}
261+
this.expanded = expanded;
262+
int iconGravity = collapsedIconGravity;
263+
int itemSpacing = collapsedItemSpacing;
264+
int itemMinHeight = collapsedItemMinHeight;
265+
int itemGravity = collapsedItemGravity;
266+
if (expanded) {
267+
iconGravity = expandedIconGravity;
268+
itemSpacing = expandedItemSpacing;
269+
itemMinHeight = expandedItemMinHeight;
270+
itemGravity = expandedItemGravity;
271+
}
272+
getNavigationRailMenuView().setItemGravity(itemGravity);
273+
super.setItemIconGravity(iconGravity);
274+
getNavigationRailMenuView().setItemSpacing(itemSpacing);
275+
getNavigationRailMenuView().setItemMinimumHeight(itemMinHeight);
276+
}
277+
200278
private void applyWindowInsets() {
201279
ViewUtils.doOnApplyWindowInsets(
202280
this,
@@ -236,11 +314,28 @@ private boolean shouldApplyWindowInsetPadding(Boolean paddingInsetFlag) {
236314
return paddingInsetFlag != null ? paddingInsetFlag : getFitsSystemWindows();
237315
}
238316

317+
private int getMaxChildWidth() {
318+
int childCount = getNavigationRailMenuView().getChildCount();
319+
int maxChildWidth = 0;
320+
for (int i = 0; i < childCount; i++) {
321+
View child = getNavigationRailMenuView().getChildAt(i);
322+
if (child.getVisibility() != GONE) {
323+
maxChildWidth = max(maxChildWidth, child.getMeasuredWidth());
324+
}
325+
}
326+
return maxChildWidth;
327+
}
328+
239329
@Override
240330
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
241331
int minWidthSpec = makeMinWidthSpec(widthMeasureSpec);
332+
if (expanded) {
333+
// Try measuring child with no other restrictions than existing measure spec
334+
measureChild(getNavigationRailMenuView(), widthMeasureSpec, heightMeasureSpec);
335+
// Measure properly with the max child width
336+
minWidthSpec = makeExpandedWidthMeasureSpec(widthMeasureSpec, getMaxChildWidth());
337+
}
242338
super.onMeasure(minWidthSpec, heightMeasureSpec);
243-
244339
if (isHeaderViewVisible()) {
245340
int maxMenuHeight = getMeasuredHeight() - headerView.getMeasuredHeight() - contentMarginTop
246341
- headerMarginBottom;
@@ -344,10 +439,9 @@ public int getMenuGravity() {
344439
return getNavigationRailMenuView().getMenuGravity();
345440
}
346441

347-
/** Get the minimum height each item in the navigation rail's menu should be. */
442+
/** Get the current minimum height each item in the navigation rail's menu should be. */
348443
public int getItemMinimumHeight() {
349-
NavigationRailMenuView menuView = (NavigationRailMenuView) getMenuView();
350-
return menuView.getItemMinimumHeight();
444+
return getNavigationRailMenuView().getItemMinimumHeight();
351445
}
352446

353447
/**
@@ -356,20 +450,38 @@ public int getItemMinimumHeight() {
356450
* <p>If this is unset (-1), each item will be at least as tall as the navigation rail is wide.
357451
*/
358452
public void setItemMinimumHeight(@Px int minHeight) {
453+
collapsedItemMinHeight = minHeight;
454+
expandedItemMinHeight = minHeight;
359455
NavigationRailMenuView menuView = (NavigationRailMenuView) getMenuView();
360456
menuView.setItemMinimumHeight(minHeight);
361457
}
362458

459+
// TODO: b/356407064 - Make public once expanded state is public
460+
private void setCollapsedItemMinimumHeight(@Px int minHeight) {
461+
collapsedItemMinHeight = minHeight;
462+
if (!expanded) {
463+
((NavigationRailMenuView) getMenuView()).setItemMinimumHeight(minHeight);
464+
}
465+
}
466+
363467
/**
364468
* Set the padding in between the navigation rail menu items.
365469
*/
366470
public void setItemSpacing(@Px int itemSpacing) {
471+
this.collapsedItemSpacing = itemSpacing;
472+
this.expandedItemSpacing = itemSpacing;
367473
getNavigationRailMenuView().setItemSpacing(itemSpacing);
368474
}
369475

370-
/**
371-
* Get the padding in between the navigation rail menu items.
372-
*/
476+
// TODO: b/356407064 - Make public once expanded state is public
477+
private void setCollapsedItemSpacing(@Px int itemSpacing) {
478+
this.collapsedItemSpacing = itemSpacing;
479+
if (!expanded) {
480+
getNavigationRailMenuView().setItemSpacing(itemSpacing);
481+
}
482+
}
483+
484+
/** Get the current padding in between the navigation rail menu items. */
373485
public int getItemSpacing() {
374486
return getNavigationRailMenuView().getItemSpacing();
375487
}
@@ -399,6 +511,17 @@ private int makeMinWidthSpec(int measureSpec) {
399511
return MeasureSpec.makeMeasureSpec(
400512
min(MeasureSpec.getSize(measureSpec), minWidth), MeasureSpec.EXACTLY);
401513
}
514+
return measureSpec;
515+
}
516+
517+
private int makeExpandedWidthMeasureSpec(int measureSpec, int measuredWidth) {
518+
int minWidth = min(minExpandedWidth, MeasureSpec.getSize(measureSpec));
519+
520+
if (MeasureSpec.getMode(measureSpec) != MeasureSpec.EXACTLY) {
521+
int newWidth = max(measuredWidth, minWidth);
522+
newWidth = max(getSuggestedMinimumWidth(), min(newWidth, maxExpandedWidth));
523+
return MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
524+
}
402525

403526
return measureSpec;
404527
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
<!-- Shape appearance overlay style reference for NavigationRail. To be used to augment
5858
attributes declared in the shapeAppearance. Attribute declaration is in the Shape package. -->
5959
<attr name="shapeAppearanceOverlay"/>
60+
<!-- Whether or not to start the NavigationRailView in its expanded state. -->
61+
<attr name="expanded" />
6062
</declare-styleable>
6163

6264
</resources>

lib/java/com/google/android/material/navigationrail/res/values/dimens.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@
4343
<dimen name="m3_navigation_rail_expanded_active_indicator_height">56dp</dimen>
4444
<dimen name="m3_navigation_rail_expanded_leading_trailing_space">20dp</dimen>
4545

46+
<dimen name="m3_navigation_rail_min_expanded_width">220dp</dimen>
47+
<dimen name="m3_navigation_rail_max_expanded_width">360dp</dimen>
48+
<dimen name="m3_navigation_rail_expanded_item_spacing">0dp</dimen>
49+
<dimen name="m3_navigation_rail_expanded_item_min_height">56dp</dimen>
50+
4651
</resources>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<attr name="ensureMinTouchTargetSize" format="boolean" />
2727
<attr name="trackColor" format="color" />
2828
<attr name="navigationIconTint" format="color"/>
29+
<attr name="expanded" format="boolean" />
2930

3031
<!-- Navigation and Pickers -->
3132
<attr name="headerLayout" format="reference"/>

0 commit comments

Comments
 (0)