Skip to content

Commit c08ee83

Browse files
imhappidsn5ft
authored andcommitted
[NavigationRail] Fixed issue where top padding was not respected if menu gravity was center or bottom in certain scenarios
PiperOrigin-RevId: 675656279
1 parent 81907eb commit c08ee83

File tree

4 files changed

+146
-42
lines changed

4 files changed

+146
-42
lines changed

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import android.view.Menu;
3939
import android.view.MenuInflater;
4040
import android.view.MenuItem;
41+
import android.view.ViewGroup;
4142
import android.widget.FrameLayout;
4243
import androidx.annotation.AttrRes;
4344
import androidx.annotation.DimenRes;
@@ -400,7 +401,9 @@ public NavigationBarView(
400401

401402
attributes.recycle();
402403

403-
addView(menuView);
404+
if (!shouldAddMenuView()) {
405+
addView(menuView);
406+
}
404407

405408
this.menu.setCallback(
406409
new MenuBuilder.Callback() {
@@ -418,6 +421,18 @@ public void onMenuModeChange(MenuBuilder menu) {}
418421
});
419422
}
420423

424+
/**
425+
* Whether or not to add the menu view; if true, the menu view is added to the NavigationBarView
426+
* in the constructor. Otherwise, the menu view should be added to the NavigationBarView as a
427+
* descendant view somewhere else.
428+
*
429+
* @hide
430+
*/
431+
@RestrictTo(LIBRARY_GROUP)
432+
public boolean shouldAddMenuView() {
433+
return false;
434+
}
435+
421436
@Override
422437
protected void onAttachedToWindow() {
423438
super.onAttachedToWindow();
@@ -476,6 +491,14 @@ public MenuView getMenuView() {
476491
return menuView;
477492
}
478493

494+
/**
495+
* Returns the {@link android.view.ViewGroup} associated with the navigation bar menu.
496+
*/
497+
@NonNull
498+
public ViewGroup getMenuViewGroup() {
499+
return menuView;
500+
}
501+
479502
/**
480503
* Inflate a menu resource into this navigation view.
481504
*
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (C) 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.android.material.navigationrail;
17+
18+
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
19+
import static java.lang.Math.max;
20+
21+
import android.content.Context;
22+
import android.view.View;
23+
import android.widget.FrameLayout;
24+
import androidx.annotation.NonNull;
25+
import androidx.annotation.RestrictTo;
26+
27+
/**
28+
* A {@link FrameLayout} implementation that lays out its children such that there is no vertical
29+
* overlap.
30+
*
31+
* @hide
32+
*/
33+
@RestrictTo(LIBRARY_GROUP)
34+
public class NavigationRailFrameLayout extends FrameLayout {
35+
36+
int paddingTop = 0;
37+
38+
public NavigationRailFrameLayout(@NonNull Context context) {
39+
super(context);
40+
}
41+
42+
public void setPaddingTop(int paddingTop) {
43+
this.paddingTop = paddingTop;
44+
}
45+
46+
@Override
47+
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
48+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
49+
int childCount = getChildCount();
50+
int totalHeaderHeight = 0;
51+
View menuView = getChildAt(0);
52+
int menuHeightSpec = heightMeasureSpec;
53+
54+
// If there's more than one child, the header should be first
55+
if (childCount > 1) {
56+
// Measure header
57+
View headerView = getChildAt(0);
58+
measureChild(headerView, widthMeasureSpec, heightMeasureSpec);
59+
LayoutParams headerLp = (LayoutParams) headerView.getLayoutParams();
60+
totalHeaderHeight =
61+
headerView.getMeasuredHeight() + headerLp.bottomMargin + headerLp.topMargin;
62+
int maxMenuHeight = getMeasuredHeight() - totalHeaderHeight - paddingTop;
63+
64+
// Measure menu
65+
menuView = getChildAt(1);
66+
menuHeightSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
67+
}
68+
LayoutParams menuLp = (LayoutParams) menuView.getLayoutParams();
69+
measureChild(menuView, widthMeasureSpec, menuHeightSpec);
70+
int totalMenuHeight = menuView.getMeasuredHeight() + menuLp.bottomMargin + menuLp.topMargin;
71+
int totalHeight = max(getMeasuredHeight(), paddingTop + totalHeaderHeight + totalMenuHeight);
72+
73+
setMeasuredDimension(getMeasuredWidth(), totalHeight);
74+
}
75+
76+
@Override
77+
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
78+
super.onLayout(changed, left, top, right, bottom);
79+
// We want to leave everything placed as is in the FrameLayout, but bumped down if it's
80+
// overlaying with something
81+
int childCount = getChildCount();
82+
int y = paddingTop;
83+
for (int i = 0; i < childCount; i++) {
84+
View child = getChildAt(i);
85+
FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
86+
y = max(y, child.getTop());
87+
y += lp.topMargin;
88+
child.layout(
89+
child.getLeft(),
90+
y,
91+
child.getRight(),
92+
y + child.getMeasuredHeight());
93+
y += child.getMeasuredHeight() + lp.bottomMargin;
94+
}
95+
}
96+
}

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import static java.lang.Math.min;
2626

2727
import android.content.Context;
28-
import android.view.Gravity;
2928
import android.view.View;
3029
import android.widget.FrameLayout;
3130
import androidx.annotation.NonNull;
@@ -218,8 +217,4 @@ public void setItemSpacing(@Px int spacing) {
218217
public int getItemSpacing() {
219218
return this.itemSpacing;
220219
}
221-
222-
boolean isTopGravity() {
223-
return (layoutParams.gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP;
224-
}
225220
}

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

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.google.android.material.R;
2020

21+
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
2122
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
2223
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
2324
import static java.lang.Math.max;
@@ -127,6 +128,7 @@ public class NavigationRailView extends NavigationBarView {
127128
@ItemIconGravity private int expandedIconGravity;
128129
@ItemGravity private int expandedItemGravity;
129130
private int expandedItemSpacing;
131+
private NavigationRailFrameLayout contentContainer;
130132

131133
public NavigationRailView(@NonNull Context context) {
132134
this(context, null);
@@ -178,6 +180,8 @@ public NavigationRailView(
178180
attributes.getDimensionPixelSize(R.styleable.NavigationRailView_headerMarginBottom,
179181
getResources().getDimensionPixelSize(R.dimen.mtrl_navigation_rail_margin));
180182

183+
addContentContainer();
184+
181185
int headerLayoutRes = attributes.getResourceId(R.styleable.NavigationRailView_headerLayout, 0);
182186
if (headerLayoutRes != 0) {
183187
addHeaderView(headerLayoutRes);
@@ -337,37 +341,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
337341
minWidthSpec = makeExpandedWidthMeasureSpec(widthMeasureSpec, getMaxChildWidth());
338342
}
339343
super.onMeasure(minWidthSpec, heightMeasureSpec);
340-
if (isHeaderViewVisible()) {
341-
int maxMenuHeight = getMeasuredHeight() - headerView.getMeasuredHeight() - contentMarginTop
342-
- headerMarginBottom;
343-
int menuHeightSpec = MeasureSpec.makeMeasureSpec(maxMenuHeight, MeasureSpec.AT_MOST);
344-
measureChild(getNavigationRailMenuView(), minWidthSpec, menuHeightSpec);
345-
}
346-
}
347-
348-
@Override
349-
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
350-
super.onLayout(changed, left, top, right, bottom);
351-
352-
NavigationRailMenuView menuView = getNavigationRailMenuView();
353-
int offsetY = 0;
354-
if (isHeaderViewVisible()) {
355-
int usedTop = headerView.getBottom() + headerMarginBottom;
356-
int menuTop = menuView.getTop();
357-
if (menuTop < usedTop) {
358-
offsetY = usedTop - menuTop;
359-
}
360-
} else if (menuView.isTopGravity()) {
361-
offsetY = contentMarginTop;
362-
}
363-
364-
if (offsetY > 0) {
365-
menuView.layout(
366-
menuView.getLeft(),
367-
menuView.getTop() + offsetY,
368-
menuView.getRight(),
369-
menuView.getBottom() + offsetY);
370-
}
344+
measureChild(
345+
contentContainer,
346+
minWidthSpec,
347+
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
371348
}
372349

373350
/**
@@ -399,8 +376,8 @@ public void addHeaderView(@NonNull View headerView) {
399376

400377
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
401378
params.gravity = DEFAULT_HEADER_GRAVITY;
402-
params.topMargin = contentMarginTop;
403-
addView(headerView, /* index= */ 0, params);
379+
params.bottomMargin = headerMarginBottom;
380+
contentContainer.addView(headerView, /* index= */ 0, params);
404381
}
405382

406383
/**
@@ -425,7 +402,7 @@ public View getHeaderView() {
425402
*/
426403
public void removeHeaderView() {
427404
if (headerView != null) {
428-
removeView(headerView);
405+
contentContainer.removeView(headerView);
429406
headerView = null;
430407
}
431408
}
@@ -527,7 +504,20 @@ private int makeExpandedWidthMeasureSpec(int measureSpec, int measuredWidth) {
527504
return measureSpec;
528505
}
529506

530-
private boolean isHeaderViewVisible() {
531-
return headerView != null && headerView.getVisibility() != View.GONE;
507+
private void addContentContainer() {
508+
View menuView = (View) getMenuView();
509+
contentContainer = new NavigationRailFrameLayout(getContext());
510+
contentContainer.setPaddingTop(contentMarginTop);
511+
contentContainer.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
512+
menuView.setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
513+
contentContainer.addView(menuView);
514+
addView(contentContainer);
515+
}
516+
517+
/** @hide */
518+
@RestrictTo(LIBRARY_GROUP)
519+
@Override
520+
public boolean shouldAddMenuView() {
521+
return true;
532522
}
533523
}

0 commit comments

Comments
 (0)