20
20
21
21
import static android .view .ViewGroup .LayoutParams .WRAP_CONTENT ;
22
22
import static androidx .annotation .RestrictTo .Scope .LIBRARY_GROUP ;
23
+ import static java .lang .Math .max ;
23
24
import static java .lang .Math .min ;
24
25
25
26
import android .content .Context ;
@@ -109,11 +110,23 @@ public class NavigationRailView extends NavigationBarView {
109
110
110
111
private final int contentMarginTop ;
111
112
private final int headerMarginBottom ;
113
+ private final int minExpandedWidth ;
114
+ private final int maxExpandedWidth ;
112
115
@ Nullable private View headerView ;
113
116
@ Nullable private Boolean paddingTopSystemWindowInsets = null ;
114
117
@ Nullable private Boolean paddingBottomSystemWindowInsets = null ;
115
118
@ Nullable private Boolean paddingStartSystemWindowInsets = null ;
116
119
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
+
117
130
public NavigationRailView (@ NonNull Context context ) {
118
131
this (context , null );
119
132
}
@@ -133,6 +146,24 @@ public NavigationRailView(
133
146
134
147
// Ensure we are using the correctly themed context rather than the context that was passed in.
135
148
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 ;
136
167
137
168
/* Custom attributes */
138
169
TintTypedArray attributes =
@@ -155,7 +186,7 @@ public NavigationRailView(
155
186
attributes .getInt (R .styleable .NavigationRailView_menuGravity , DEFAULT_MENU_GRAVITY ));
156
187
157
188
if (attributes .hasValue (R .styleable .NavigationRailView_itemMinHeight )) {
158
- setItemMinimumHeight (
189
+ setCollapsedItemMinimumHeight (
159
190
attributes .getDimensionPixelSize (
160
191
R .styleable .NavigationRailView_itemMinHeight , NO_ITEM_MINIMUM_HEIGHT ));
161
192
}
@@ -189,14 +220,61 @@ public NavigationRailView(
189
220
AnimationUtils .lerp (getItemPaddingBottom (), largeFontBottomPadding , progress );
190
221
setItemPaddingTop (Math .round (topPadding ));
191
222
setItemPaddingBottom (Math .round (bottomPadding ));
192
- setItemSpacing (
223
+ setCollapsedItemSpacing (
193
224
attributes .getDimensionPixelSize (R .styleable .NavigationRailView_itemSpacing , 0 ));
194
225
226
+ setExpanded (attributes .getBoolean (R .styleable .NavigationRailView_expanded , false ));
227
+
195
228
attributes .recycle ();
196
229
197
230
applyWindowInsets ();
198
231
}
199
232
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
+
200
278
private void applyWindowInsets () {
201
279
ViewUtils .doOnApplyWindowInsets (
202
280
this ,
@@ -236,11 +314,28 @@ private boolean shouldApplyWindowInsetPadding(Boolean paddingInsetFlag) {
236
314
return paddingInsetFlag != null ? paddingInsetFlag : getFitsSystemWindows ();
237
315
}
238
316
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
+
239
329
@ Override
240
330
protected void onMeasure (int widthMeasureSpec , int heightMeasureSpec ) {
241
331
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
+ }
242
338
super .onMeasure (minWidthSpec , heightMeasureSpec );
243
-
244
339
if (isHeaderViewVisible ()) {
245
340
int maxMenuHeight = getMeasuredHeight () - headerView .getMeasuredHeight () - contentMarginTop
246
341
- headerMarginBottom ;
@@ -344,10 +439,9 @@ public int getMenuGravity() {
344
439
return getNavigationRailMenuView ().getMenuGravity ();
345
440
}
346
441
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. */
348
443
public int getItemMinimumHeight () {
349
- NavigationRailMenuView menuView = (NavigationRailMenuView ) getMenuView ();
350
- return menuView .getItemMinimumHeight ();
444
+ return getNavigationRailMenuView ().getItemMinimumHeight ();
351
445
}
352
446
353
447
/**
@@ -356,20 +450,38 @@ public int getItemMinimumHeight() {
356
450
* <p>If this is unset (-1), each item will be at least as tall as the navigation rail is wide.
357
451
*/
358
452
public void setItemMinimumHeight (@ Px int minHeight ) {
453
+ collapsedItemMinHeight = minHeight ;
454
+ expandedItemMinHeight = minHeight ;
359
455
NavigationRailMenuView menuView = (NavigationRailMenuView ) getMenuView ();
360
456
menuView .setItemMinimumHeight (minHeight );
361
457
}
362
458
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
+
363
467
/**
364
468
* Set the padding in between the navigation rail menu items.
365
469
*/
366
470
public void setItemSpacing (@ Px int itemSpacing ) {
471
+ this .collapsedItemSpacing = itemSpacing ;
472
+ this .expandedItemSpacing = itemSpacing ;
367
473
getNavigationRailMenuView ().setItemSpacing (itemSpacing );
368
474
}
369
475
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. */
373
485
public int getItemSpacing () {
374
486
return getNavigationRailMenuView ().getItemSpacing ();
375
487
}
@@ -399,6 +511,17 @@ private int makeMinWidthSpec(int measureSpec) {
399
511
return MeasureSpec .makeMeasureSpec (
400
512
min (MeasureSpec .getSize (measureSpec ), minWidth ), MeasureSpec .EXACTLY );
401
513
}
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
+ }
402
525
403
526
return measureSpec ;
404
527
}
0 commit comments