2020
2121import static android .view .ViewGroup .LayoutParams .WRAP_CONTENT ;
2222import static androidx .annotation .RestrictTo .Scope .LIBRARY_GROUP ;
23+ import static java .lang .Math .max ;
2324import static java .lang .Math .min ;
2425
2526import 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 }
0 commit comments