2020import static android .view .ViewGroup .LayoutParams .WRAP_CONTENT ;
2121import static androidx .annotation .RestrictTo .Scope .LIBRARY_GROUP ;
2222import static com .google .android .material .navigationrail .NavigationRailView .DEFAULT_MENU_GRAVITY ;
23+ import static java .lang .Math .max ;
2324import static java .lang .Math .min ;
2425
2526import android .content .Context ;
26- import androidx .appcompat .view .menu .MenuBuilder ;
2727import android .view .Gravity ;
2828import android .view .View ;
29- import android .view .ViewGroup ;
3029import android .widget .FrameLayout ;
3130import androidx .annotation .NonNull ;
3231import androidx .annotation .RestrictTo ;
@@ -49,35 +48,21 @@ public NavigationRailMenuView(@NonNull Context context) {
4948
5049 @ Override
5150 protected void onMeasure (int widthMeasureSpec , int heightMeasureSpec ) {
52- int childHeightSpec = makeSharedHeightSpec (widthMeasureSpec , heightMeasureSpec );
53-
54- int childCount = getChildCount ();
55- int maxWidth = 0 ;
56- int totalHeight = 0 ;
57- for (int i = 0 ; i < childCount ; i ++) {
58- final View child = getChildAt (i );
59- if (child .getVisibility () != GONE ) {
60- child .measure (widthMeasureSpec , childHeightSpec );
61- ViewGroup .LayoutParams params = child .getLayoutParams ();
62- params .width = child .getMeasuredWidth ();
63- params .height = child .getMeasuredHeight ();
64- totalHeight += params .height ;
65- if (params .width > maxWidth ) {
66- maxWidth = params .width ;
67- }
68- }
51+ int maxHeight = MeasureSpec .getSize (heightMeasureSpec );
52+ int visibleCount = getMenu ().getVisibleItems ().size ();
53+
54+ int measuredHeight ;
55+ if (visibleCount > 1 && isShifting (getLabelVisibilityMode (), visibleCount )) {
56+ measuredHeight = measureShiftingChildHeights (widthMeasureSpec , maxHeight , visibleCount );
57+ } else {
58+ measuredHeight = measureSharedChildHeights (widthMeasureSpec , maxHeight , visibleCount , null );
6959 }
7060
71- // Set view to use a fixed width, but wrap all item heights
61+ // Set view to use parent width, but wrap all item heights
62+ int parentWidth = MeasureSpec .getSize (widthMeasureSpec );
7263 setMeasuredDimension (
73- View .resolveSizeAndState (
74- maxWidth ,
75- MeasureSpec .makeMeasureSpec (maxWidth , MeasureSpec .EXACTLY ),
76- /* childMeasuredState= */ 0 ),
77- View .resolveSizeAndState (
78- totalHeight ,
79- MeasureSpec .makeMeasureSpec (totalHeight , MeasureSpec .EXACTLY ),
80- /* childMeasuredState= */ 0 ));
64+ View .resolveSizeAndState (parentWidth , widthMeasureSpec , /* childMeasuredState= */ 0 ),
65+ View .resolveSizeAndState (measuredHeight , heightMeasureSpec , /* childMeasuredState= */ 0 ));
8166 }
8267
8368 @ Override
@@ -101,14 +86,59 @@ protected NavigationBarItemView createNavigationBarItemView(@NonNull Context con
10186 return new NavigationRailItemView (context );
10287 }
10388
104- private int makeSharedHeightSpec (int parentWidthSpec , int parentHeightSpec ) {
105- MenuBuilder menu = getMenu ();
106- int visibleCount = menu .getVisibleItems ().size ();
107- int maxHeight = MeasureSpec .getSize (parentHeightSpec );
108- int maxAvailable = maxHeight / (visibleCount == 0 ? 1 : visibleCount );
109-
89+ private int makeSharedHeightSpec (int parentWidthSpec , int maxHeight , int shareCount ) {
90+ int maxAvailable = maxHeight / max (1 , shareCount );
11091 return MeasureSpec .makeMeasureSpec (
111- min (MeasureSpec .getSize (parentWidthSpec ), maxAvailable ), MeasureSpec .EXACTLY );
92+ min (MeasureSpec .getSize (parentWidthSpec ), maxAvailable ), MeasureSpec .UNSPECIFIED );
93+ }
94+
95+ private int measureShiftingChildHeights (int widthMeasureSpec , int maxHeight , int shareCount ) {
96+ int selectedViewHeight = 0 ;
97+
98+ View selectedView = getChildAt (getSelectedItemPosition ());
99+ if (selectedView != null ) {
100+ int childHeightSpec = makeSharedHeightSpec (widthMeasureSpec , maxHeight , shareCount );
101+ selectedViewHeight = measureChildHeight (selectedView , widthMeasureSpec , childHeightSpec );
102+ maxHeight -= selectedViewHeight ;
103+ --shareCount ;
104+ }
105+
106+ return selectedViewHeight
107+ + measureSharedChildHeights (widthMeasureSpec , maxHeight , shareCount , selectedView );
108+ }
109+
110+ private int measureSharedChildHeights (
111+ int widthMeasureSpec , int maxHeight , int shareCount , View selectedView ) {
112+ int childHeightSpec = makeSharedHeightSpec (widthMeasureSpec , maxHeight , shareCount );
113+ if (selectedView == null ) {
114+ childHeightSpec = makeSharedHeightSpec (widthMeasureSpec , maxHeight , shareCount );
115+ } else {
116+ // Use the same height for the unselected views, so the items do not have different heights
117+ // This may cause the last time to overflow and get cropped, but the developer is expected to
118+ // ensure that there is enough height for the rail or place it inside scroll view.
119+ childHeightSpec =
120+ MeasureSpec .makeMeasureSpec (selectedView .getMeasuredHeight (), MeasureSpec .UNSPECIFIED );
121+ }
122+
123+ int childCount = getChildCount ();
124+ int totalHeight = 0 ;
125+ for (int i = 0 ; i < childCount ; i ++) {
126+ final View child = getChildAt (i );
127+ if (child != selectedView ) {
128+ totalHeight += measureChildHeight (child , widthMeasureSpec , childHeightSpec );
129+ }
130+ }
131+
132+ return totalHeight ;
133+ }
134+
135+ private int measureChildHeight (View child , int widthMeasureSpec , int heightMeasureSpec ) {
136+ if (child .getVisibility () != GONE ) {
137+ child .measure (widthMeasureSpec , heightMeasureSpec );
138+ return child .getMeasuredHeight ();
139+ }
140+
141+ return 0 ;
112142 }
113143
114144 void setMenuGravity (int gravity ) {
0 commit comments