diff --git a/docs/components/NavigationDrawer.md b/docs/components/NavigationDrawer.md
index 27ffc106b0c..eb122e4abbf 100644
--- a/docs/components/NavigationDrawer.md
+++ b/docs/components/NavigationDrawer.md
@@ -254,13 +254,16 @@ subtitles, and an optional scrim.
### Container attributes
-Element | Attribute(s) | Related method(s) | Default value
------------------------ | ------------------------------------------------------------------- | ------------------------------------------------ | -------------
-**Color** | `android:background` | `setBackground`
`getBackground` | `?attr/colorSurface`
-**Shape** | `app:shapeAppearance`
`app:shapeAppearanceOverlay` | N/A | `null`
-**Elevation** | `app:elevation` (can be used on `NavigationView` or `DrawerLayout`) | `setElevation`
`getElevation` | `0dp` (`NavigationView`) or `1dp` (`DrawerLayout`)
-**Max width** | `android:maxWidth` | N/A | `280dp`
-**Fits system windows** | `android:fitsSystemWindows` | `setFitsSystemWindows`
`getFitsSystemWindows` | `true`
+Element | Attribute(s) | Related method(s) | Default value
+----------------------- |---------------------------------------------------------------------|----------------------------------------------------------------------------------| -------------
+**Color** | `android:background` | `setBackground`
`getBackground` | `?attr/colorSurface`
+**Shape** | `app:shapeAppearance`
`app:shapeAppearanceOverlay` | N/A | `null`
+**Elevation** | `app:elevation` (can be used on `NavigationView` or `DrawerLayout`) | `setElevation`
`getElevation` | `0dp` (`NavigationView`) or `1dp` (`DrawerLayout`)
+**Max width** | `android:maxWidth` | N/A | `280dp`
+**Fits system windows** | `android:fitsSystemWindows` | `setFitsSystemWindows`
`getFitsSystemWindows` | `true`
+**Drawer corner size** | `drawerLayoutCornerSize` | N/A | `16dp`
+**Drawer corner clipping** | `drawerLayoutCornerClippingEnabled` | `setDrawerLayoutCornerClippingEnabled`
`isDrawerLayoutCornerClippingEnabled` | `false`
+
### Header attributes
diff --git a/lib/java/com/google/android/material/navigation/NavigationView.java b/lib/java/com/google/android/material/navigation/NavigationView.java
index 87f44898391..3a4f98034f5 100644
--- a/lib/java/com/google/android/material/navigation/NavigationView.java
+++ b/lib/java/com/google/android/material/navigation/NavigationView.java
@@ -130,6 +130,7 @@ public class NavigationView extends ScrimInsetsFrameLayout {
private int layoutGravity = Gravity.NO_GRAVITY;
@Px private int drawerLayoutCornerSize = 0;
+ private boolean drawerLayoutCornerClippingEnabled = false;
@Nullable private Path shapeClipPath;
private final RectF shapeClipBounds = new RectF();
@@ -153,11 +154,7 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
// Custom attributes
TintTypedArray a =
ThemeEnforcement.obtainTintedStyledAttributes(
- context,
- attrs,
- R.styleable.NavigationView,
- defStyleAttr,
- DEF_STYLE_RES);
+ context, attrs, R.styleable.NavigationView, defStyleAttr, DEF_STYLE_RES);
if (a.hasValue(R.styleable.NavigationView_android_background)) {
ViewCompat.setBackground(this, a.getDrawable(R.styleable.NavigationView_android_background));
@@ -166,9 +163,12 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
// Get the drawer layout corner size and layout gravity to be used to shape the exposed corners
// of this view when placed inside a drawer layout.
drawerLayoutCornerSize =
- a.getDimensionPixelSize(
- R.styleable.NavigationView_drawerLayoutCornerSize, 0);
+ a.getDimensionPixelSize(R.styleable.NavigationView_drawerLayoutCornerSize, 0);
layoutGravity = a.getInt(R.styleable.NavigationView_android_layout_gravity, Gravity.NO_GRAVITY);
+ setDrawerLayoutCornerClippingEnabled(
+ a.getBoolean(
+ R.styleable.NavigationView_drawerLayoutCornerClippingEnabled,
+ drawerLayoutCornerClippingEnabled));
// Set the background to a MaterialShapeDrawable if it hasn't been set or if it can be converted
// to a MaterialShapeDrawable.
@@ -204,7 +204,7 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
}
if (subheaderTextAppearance == NavigationMenuPresenter.NO_TEXT_APPEARANCE_SET
- && subheaderColor == null) {
+ && subheaderColor == null) {
// If there isn't a text appearance set, we'll use a default text color
subheaderColor = createDefaultColorStateList(android.R.attr.textColorSecondary);
}
@@ -241,18 +241,18 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
if (itemBackground == null && hasShapeAppearance(a)) {
itemBackground = createDefaultItemBackground(a);
- ColorStateList itemRippleColor = MaterialResources.getColorStateList(
- context, a, R.styleable.NavigationView_itemRippleColor);
+ ColorStateList itemRippleColor =
+ MaterialResources.getColorStateList(
+ context, a, R.styleable.NavigationView_itemRippleColor);
// Use a ripple matching the item's shape as the foreground for api level 21+ and if a ripple
// color is set. Otherwise the selectableItemBackground foreground from the item layout will
// be used
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && itemRippleColor != null) {
Drawable itemRippleMask = createDefaultItemDrawable(a, null);
- RippleDrawable ripple = new RippleDrawable(
- RippleUtils.sanitizeRippleDrawableColor(itemRippleColor),
- null,
- itemRippleMask);
+ RippleDrawable ripple =
+ new RippleDrawable(
+ RippleUtils.sanitizeRippleDrawableColor(itemRippleColor), null, itemRippleMask);
presenter.setItemForeground(ripple);
}
}
@@ -289,8 +289,7 @@ public NavigationView(@NonNull Context context, @Nullable AttributeSet attrs, in
a.getBoolean(R.styleable.NavigationView_topInsetScrimEnabled, topInsetScrimEnabled));
setBottomInsetScrimEnabled(
- a.getBoolean(R.styleable.NavigationView_bottomInsetScrimEnabled, bottomInsetScrimEnabled)
- );
+ a.getBoolean(R.styleable.NavigationView_bottomInsetScrimEnabled, bottomInsetScrimEnabled));
final int itemIconPadding =
a.getDimensionPixelSize(R.styleable.NavigationView_itemIconPadding, 0);
@@ -345,6 +344,37 @@ public void setOverScrollMode(int overScrollMode) {
}
}
+ /**
+ * Gets whether NavigationView will clip itself and its children to its shape appearance and
+ * drawer layout corner size.
+ *
+ *
See {@link #setDrawerLayoutCornerClippingEnabled(boolean)}. + * + * @return true if this view will clip itself and its children to its shape appearance and drawer + * layout corner size + */ + public boolean isDrawerLayoutCornerClippingEnabled() { + return drawerLayoutCornerClippingEnabled; + } + + /** + * Sets whether NavigationView should clip itself and its children to its shape appearance and + * drawer layout corner size. + * + *
Clipping uses {@link Canvas#clipPath(Path)} which is expensive and should be used only when
+ * necessary. The most common example of this is when using a header layout with a full bleed
+ * image or other content that would obscure the top end corner shape.
+ *
+ * @param enabled true if navigation view should use canvas clipping to clip itself and all
+ * children to its shape appearance and drawer layout corner size.
+ */
+ public void setDrawerLayoutCornerClippingEnabled(boolean enabled) {
+ if (drawerLayoutCornerClippingEnabled != enabled) {
+ drawerLayoutCornerClippingEnabled = enabled;
+ invalidate();
+ }
+ }
+
/**
* Determine whether this view is placed inside a drawer layout and should have its exposed
* corners shaped according to the app:drawerLayoutCornerSize
attribute.
@@ -357,8 +387,7 @@ private void maybeUpdateCornerSizeForDrawerLayout(@Px int width, @Px int height)
&& getBackground() instanceof MaterialShapeDrawable) {
// Get the absolute gravity of this view and set the top and bottom exposed corner sizes.
MaterialShapeDrawable background = (MaterialShapeDrawable) getBackground();
- ShapeAppearanceModel.Builder builder =
- background.getShapeAppearanceModel().toBuilder();
+ ShapeAppearanceModel.Builder builder = background.getShapeAppearanceModel().toBuilder();
int absGravity =
GravityCompat.getAbsoluteGravity(layoutGravity, ViewCompat.getLayoutDirection(this));
if (absGravity == Gravity.LEFT) {
@@ -423,8 +452,9 @@ public void setElevation(float elevation) {
*/
@NonNull
private Drawable createDefaultItemBackground(@NonNull TintTypedArray a) {
- ColorStateList fillColor = MaterialResources.getColorStateList(
- getContext(), a, R.styleable.NavigationView_itemShapeFillColor);
+ ColorStateList fillColor =
+ MaterialResources.getColorStateList(
+ getContext(), a, R.styleable.NavigationView_itemShapeFillColor);
return createDefaultItemDrawable(a, fillColor);
}
@@ -437,7 +467,7 @@ private Drawable createDefaultItemDrawable(
MaterialShapeDrawable materialShapeDrawable =
new MaterialShapeDrawable(
ShapeAppearanceModel.builder(
- getContext(), shapeAppearanceResId, shapeAppearanceOverlayResId)
+ getContext(), shapeAppearanceResId, shapeAppearanceOverlayResId)
.build());
materialShapeDrawable.setFillColor(fillColor);
@@ -499,7 +529,7 @@ protected void onMeasure(int widthSpec, int heightSpec) {
@Override
protected void dispatchDraw(@NonNull Canvas canvas) {
- if (shapeClipPath == null) {
+ if (shapeClipPath == null || !isDrawerLayoutCornerClippingEnabled()) {
super.dispatchDraw(canvas);
return;
}
@@ -822,16 +852,14 @@ public int getItemMaxLines() {
return presenter.getItemMaxLines();
}
- /**
- * Whether or not the NavigationView will draw a scrim behind the window's top inset.
- */
+ /** Whether or not the NavigationView will draw a scrim behind the window's top inset. */
public boolean isTopInsetScrimEnabled() {
return this.topInsetScrimEnabled;
}
/**
- * Set whether or not the NavigationView should draw a scrim behind the window's top
- * inset (typically the status bar).
+ * Set whether or not the NavigationView should draw a scrim behind the window's top inset
+ * (typically the status bar).
*
* @param enabled true when the NavigationView should draw a scrim.
*/
@@ -839,16 +867,14 @@ public void setTopInsetScrimEnabled(boolean enabled) {
this.topInsetScrimEnabled = enabled;
}
- /**
- * Whether or not the NavigationView will draw a scrim behind the window's bottom inset.
- */
+ /** Whether or not the NavigationView will draw a scrim behind the window's bottom inset. */
public boolean isBottomInsetScrimEnabled() {
return this.bottomInsetScrimEnabled;
}
/**
- * Set whether or not the NavigationView should draw a scrim behind the window's bottom
- * inset (typically the navigation bar)
+ * Set whether or not the NavigationView should draw a scrim behind the window's bottom inset
+ * (typically the navigation bar)
*
* @param enabled true when the NavigationView should draw a scrim.
*/
@@ -871,47 +897,35 @@ public void setDividerInsetStart(@Px int dividerInsetStart) {
presenter.setDividerInsetStart(dividerInsetStart);
}
- /**
- * Get the distance between the end of a divider and the end of the NavigationView.
- */
+ /** Get the distance between the end of a divider and the end of the NavigationView. */
@Px
public int getDividerInsetEnd() {
return presenter.getDividerInsetEnd();
}
- /**
- * Set the distance between the end of a divider and the end of the NavigationView.
- */
+ /** Set the distance between the end of a divider and the end of the NavigationView. */
public void setDividerInsetEnd(@Px int dividerInsetEnd) {
presenter.setDividerInsetEnd(dividerInsetEnd);
}
- /**
- * Get the distance between the start of the NavigationView and the start of a menu subheader.
- */
+ /** Get the distance between the start of the NavigationView and the start of a menu subheader. */
@Px
public int getSubheaderInsetStart() {
return presenter.getSubheaderInsetStart();
}
- /**
- * Set the distance between the start of the NavigationView and the start of a menu subheader.
- */
+ /** Set the distance between the start of the NavigationView and the start of a menu subheader. */
public void setSubheaderInsetStart(@Px int subheaderInsetStart) {
presenter.setSubheaderInsetStart(subheaderInsetStart);
}
- /**
- * Get the distance between the end of a menu subheader and the end of the NavigationView.
- */
+ /** Get the distance between the end of a menu subheader and the end of the NavigationView. */
@Px
public int getSubheaderInsetEnd() {
return presenter.getSubheaderInsetEnd();
}
- /**
- * Set the distance between the end of a menu subheader and the end of the NavigationView.
- */
+ /** Set the distance between the end of a menu subheader and the end of the NavigationView. */
public void setSubheaderInsetEnd(@Px int subheaderInsetEnd) {
presenter.setSubheaderInsetEnd(subheaderInsetEnd);
}
@@ -978,8 +992,7 @@ public void onGlobalLayout() {
if (activity != null && VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
Rect displayBounds = WindowUtils.getCurrentWindowBounds(activity);
- boolean isBehindSystemNav =
- displayBounds.height() - getHeight() == tmpLocation[1];
+ boolean isBehindSystemNav = displayBounds.height() - getHeight() == tmpLocation[1];
boolean hasNonZeroAlpha =
Color.alpha(activity.getWindow().getNavigationBarColor()) != 0;
setDrawBottomInsetForeground(
@@ -996,9 +1009,7 @@ public void onGlobalLayout() {
}
};
- getViewTreeObserver()
- .addOnGlobalLayoutListener(
- onGlobalLayoutListener);
+ getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
/** Listener for handling events on navigation items. */
diff --git a/lib/java/com/google/android/material/navigation/res-public/values/public.xml b/lib/java/com/google/android/material/navigation/res-public/values/public.xml
index d546ec464de..633f33368a5 100644
--- a/lib/java/com/google/android/material/navigation/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/navigation/res-public/values/public.xml
@@ -44,6 +44,7 @@