Skip to content

Commit 16822d6

Browse files
melaniegoetzdsn5ft
authored andcommitted
Shape Theming for BottomSheet
PiperOrigin-RevId: 224370498
1 parent fa528c4 commit 16822d6

File tree

5 files changed

+103
-3
lines changed

5 files changed

+103
-3
lines changed

lib/java/com/google/android/material/bottomsheet/BottomSheetBehavior.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@
2121
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
2222

2323
import android.content.Context;
24+
import android.content.res.ColorStateList;
2425
import android.content.res.TypedArray;
2526
import android.os.Build;
2627
import android.os.Parcel;
2728
import android.os.Parcelable;
2829
import android.support.annotation.IntDef;
2930
import android.support.annotation.NonNull;
31+
import android.support.annotation.Nullable;
3032
import android.support.annotation.RestrictTo;
3133
import android.support.annotation.VisibleForTesting;
34+
import com.google.android.material.resources.MaterialResources;
35+
import com.google.android.material.shape.MaterialShapeDrawable;
36+
import com.google.android.material.shape.ShapeAppearanceModel;
3237
import android.support.design.widget.CoordinatorLayout;
3338
import android.support.v4.math.MathUtils;
3439
import android.support.v4.view.AbsSavedState;
@@ -138,6 +143,14 @@ public abstract static class BottomSheetCallback {
138143
/** The last peek height calculated in onLayoutChild. */
139144
private int lastPeekHeight;
140145

146+
/** True if Behavior has a non-null value for the shapeAppearance attribute */
147+
private boolean shapeThemingEnabled;
148+
149+
private MaterialShapeDrawable materialShapeDrawable;
150+
151+
/** Default Shape Appearance to be used in bottomsheet */
152+
private ShapeAppearanceModel shapeAppearanceModelDefault;
153+
141154
int fitToContentsOffset;
142155

143156
int halfExpandedOffset;
@@ -181,6 +194,16 @@ public BottomSheetBehavior() {}
181194
public BottomSheetBehavior(Context context, AttributeSet attrs) {
182195
super(context, attrs);
183196
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BottomSheetBehavior_Layout);
197+
this.shapeThemingEnabled = a.hasValue(R.styleable.BottomSheetBehavior_Layout_shapeAppearance);
198+
boolean hasBackgroundTint = a.hasValue(R.styleable.BottomSheetBehavior_Layout_backgroundTint);
199+
if (hasBackgroundTint) {
200+
ColorStateList bottomSheetColor =
201+
MaterialResources.getColorStateList(
202+
context, a, R.styleable.BottomSheetBehavior_Layout_backgroundTint);
203+
createMaterialShapeDrawable(context, attrs, hasBackgroundTint, bottomSheetColor);
204+
} else {
205+
createMaterialShapeDrawable(context, attrs, hasBackgroundTint, null);
206+
}
184207
TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight);
185208
if (value != null && value.data == PEEK_HEIGHT_AUTO) {
186209
setPeekHeight(value.data);
@@ -221,6 +244,12 @@ public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirect
221244
if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) {
222245
child.setFitsSystemWindows(true);
223246
}
247+
// Only set MaterialShapeDrawable as background if shapeTheming is enabled, otherwise will
248+
// default to android:background declared in styles or layout.
249+
if (shapeThemingEnabled && materialShapeDrawable != null) {
250+
ViewCompat.setBackground(child, materialShapeDrawable);
251+
}
252+
224253
int savedTop = child.getTop();
225254
// First let the parent lay it out
226255
parent.onLayoutChild(child, layoutDirection);
@@ -708,11 +737,27 @@ void setStateInternal(@State int state) {
708737
bottomSheet, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
709738
bottomSheet.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
710739

740+
updateDrawableOnStateChange(state);
711741
if (callback != null) {
712742
callback.onStateChanged(bottomSheet, state);
713743
}
714744
}
715745

746+
private void updateDrawableOnStateChange(@State int state) {
747+
if (materialShapeDrawable != null) {
748+
if (state == STATE_EXPANDED && (parentHeight <= viewRef.get().getHeight())) {
749+
// If the bottomsheet is fully expanded, change ShapeAppearance to sharp corners to
750+
// indicate the bottomsheet has no more content to scroll.
751+
// Overriding of this style may be performed in the bottomsheet callback.
752+
materialShapeDrawable.getShapeAppearanceModel().setCornerRadius(0);
753+
materialShapeDrawable.invalidateSelf();
754+
}
755+
if (state == STATE_COLLAPSED || state == STATE_DRAGGING) {
756+
materialShapeDrawable.setShapeAppearanceModel(shapeAppearanceModelDefault);
757+
}
758+
}
759+
}
760+
716761
private void calculateCollapsedOffset() {
717762
if (fitToContents) {
718763
collapsedOffset = Math.max(parentHeight - lastPeekHeight, fitToContentsOffset);
@@ -758,6 +803,32 @@ View findScrollingChild(View view) {
758803
return null;
759804
}
760805

806+
private void createMaterialShapeDrawable(
807+
Context context,
808+
AttributeSet attrs,
809+
boolean hasBackgroundTint,
810+
@Nullable ColorStateList bottomSheetColor) {
811+
if (this.shapeThemingEnabled) {
812+
this.shapeAppearanceModelDefault = new ShapeAppearanceModel(
813+
context,
814+
attrs,
815+
R.styleable.BottomSheetBehavior_Layout_shapeAppearance,
816+
R.styleable.BottomSheetBehavior_Layout_shapeAppearanceOverlay);
817+
818+
this.materialShapeDrawable =
819+
new MaterialShapeDrawable(shapeAppearanceModelDefault);
820+
821+
if (hasBackgroundTint && bottomSheetColor != null) {
822+
materialShapeDrawable.setFillColor(bottomSheetColor);
823+
} else {
824+
// If the tint isn't set, use the theme default background color.
825+
TypedValue defaultColor = new TypedValue();
826+
context.getTheme().resolveAttribute(android.R.attr.colorBackground, defaultColor, true);
827+
materialShapeDrawable.setTint(defaultColor.data);
828+
}
829+
}
830+
}
831+
761832
private float getYVelocity() {
762833
if (velocityTracker == null) {
763834
return 0;

lib/java/com/google/android/material/bottomsheet/res-public/values/public.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<public name="bottomSheetDialogTheme" type="attr"/>
2323
<public name="bottomSheetStyle" type="attr"/>
2424
<public name="behavior_peekHeight" type="attr"/>
25+
<public name="backgroundTint" type="attr"/>
2526
<public name="behavior_fitToContents" type="attr"/>
2627
<public name="Animation.Design.BottomSheetDialog" type="style"/>
2728
<public name="Widget.Design.BottomSheet.Modal" type="style"/>

lib/java/com/google/android/material/bottomsheet/res/values/attrs.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@
3333
<attr name="behavior_skipCollapsed" format="boolean"/>
3434
<!-- Whether height of expanded sheet wraps content or not -->
3535
<attr name="behavior_fitToContents" format="boolean"/>
36+
<!-- Shape appearance style reference for BottomSheet. Attribute declaration is in the shape
37+
package. -->
38+
<attr name="shapeAppearance"/>
39+
<!-- Shape appearance overlay style reference for BottomSheet. To be used to augment attributes
40+
declared in the shapeAppearance. Attribute declaration is in the shape package. -->
41+
<attr name="shapeAppearanceOverlay"/>
42+
<!-- Background color used by the BottomSheetBehavior background drawable when shape theming is
43+
enabled. Accepts a ColorStateList or ColorInt. If shape theming is not enabled,
44+
android:background should instead be utilized to set the background resource. -->
45+
<attr name="backgroundTint"/>
3646
</declare-styleable>
3747

3848
</resources>

lib/java/com/google/android/material/bottomsheet/res/values/styles.xml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,32 @@
2727
</style>
2828

2929
<style name="Widget.Design.BottomSheet.Modal" parent="android:Widget">
30+
<item name="enforceMaterialTheme">false</item>
3031
<item name="android:background">?android:attr/colorBackground</item>
3132
<item name="android:elevation" tools:ignore="NewApi">
3233
@dimen/design_bottom_sheet_modal_elevation
3334
</item>
3435
<item name="behavior_peekHeight">auto</item>
3536
<item name="behavior_hideable">true</item>
3637
<item name="behavior_skipCollapsed">false</item>
38+
<item name="shapeAppearance">@null</item>
39+
<item name="shapeAppearanceOverlay">@null</item>
40+
<item name="backgroundTint">?android:attr/colorBackground</item>
3741
</style>
3842

39-
<style name="Widget.MaterialComponents.BottomSheet.Modal" parent="Widget.Design.BottomSheet.Modal" />
43+
<style name="Widget.MaterialComponents.BottomSheet.Modal" parent="Widget.Design.BottomSheet.Modal">
44+
<item name="android:background">@null</item>
45+
<item name="enforceMaterialTheme">true</item>
46+
<item name="shapeAppearance">?attr/shapeAppearanceLargeComponent</item>
47+
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.MaterialComponents.BottomSheet.Modal</item>
48+
<item name="backgroundTint">?android:attr/colorBackground</item>
49+
</style>
50+
51+
<style name="ShapeAppearanceOverlay.MaterialComponents.BottomSheet.Modal" parent="">
52+
<item name="cornerSizeTopRight">0dp</item>
53+
<item name="cornerSizeTopLeft">0dp</item>
54+
<item name="cornerFamilyTopLeft">rounded</item>
55+
<item name="cornerFamilyTopRight">rounded</item>
56+
</style>
4057

41-
</resources>
58+
</resources>

lib/java/com/google/android/material/shape/ShapeAppearanceModel.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ public ShapeAppearanceModel(
8787
TypedArray a =
8888
context.obtainStyledAttributes(attrs, R.styleable.MaterialShape, defStyleAttr, defStyleRes);
8989

90-
int shapeAppearanceResId = a.getResourceId(R.styleable.MaterialShape_shapeAppearance, 0);
90+
int shapeAppearanceResId =
91+
a.getResourceId(R.styleable.MaterialShape_shapeAppearance, 0);
9192
int shapeAppearanceOverlayResId =
9293
a.getResourceId(R.styleable.MaterialShape_shapeAppearanceOverlay, 0);
9394
a.recycle();

0 commit comments

Comments
 (0)