|
21 | 21 | import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; |
22 | 22 |
|
23 | 23 | import android.content.Context; |
| 24 | +import android.content.res.ColorStateList; |
24 | 25 | import android.content.res.TypedArray; |
25 | 26 | import android.os.Build; |
26 | 27 | import android.os.Parcel; |
27 | 28 | import android.os.Parcelable; |
28 | 29 | import android.support.annotation.IntDef; |
29 | 30 | import android.support.annotation.NonNull; |
| 31 | +import android.support.annotation.Nullable; |
30 | 32 | import android.support.annotation.RestrictTo; |
31 | 33 | 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; |
32 | 37 | import android.support.design.widget.CoordinatorLayout; |
33 | 38 | import android.support.v4.math.MathUtils; |
34 | 39 | import android.support.v4.view.AbsSavedState; |
@@ -138,6 +143,16 @@ public abstract static class BottomSheetCallback { |
138 | 143 | /** The last peek height calculated in onLayoutChild. */ |
139 | 144 | private int lastPeekHeight; |
140 | 145 |
|
| 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 | + |
| 154 | + private static final int DEF_STYLE_RES = R.style.Widget_Design_BottomSheet_Modal; |
| 155 | + |
141 | 156 | int fitToContentsOffset; |
142 | 157 |
|
143 | 158 | int halfExpandedOffset; |
@@ -181,6 +196,16 @@ public BottomSheetBehavior() {} |
181 | 196 | public BottomSheetBehavior(Context context, AttributeSet attrs) { |
182 | 197 | super(context, attrs); |
183 | 198 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BottomSheetBehavior_Layout); |
| 199 | + this.shapeThemingEnabled = a.hasValue(R.styleable.BottomSheetBehavior_Layout_shapeAppearance); |
| 200 | + boolean hasBackgroundTint = a.hasValue(R.styleable.BottomSheetBehavior_Layout_backgroundTint); |
| 201 | + if (hasBackgroundTint) { |
| 202 | + ColorStateList bottomSheetColor = |
| 203 | + MaterialResources.getColorStateList( |
| 204 | + context, a, R.styleable.BottomSheetBehavior_Layout_backgroundTint); |
| 205 | + createMaterialShapeDrawable(context, attrs, hasBackgroundTint, bottomSheetColor); |
| 206 | + } else { |
| 207 | + createMaterialShapeDrawable(context, attrs, hasBackgroundTint); |
| 208 | + } |
184 | 209 | TypedValue value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight); |
185 | 210 | if (value != null && value.data == PEEK_HEIGHT_AUTO) { |
186 | 211 | setPeekHeight(value.data); |
@@ -221,6 +246,12 @@ public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirect |
221 | 246 | if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) { |
222 | 247 | child.setFitsSystemWindows(true); |
223 | 248 | } |
| 249 | + // Only set MaterialShapeDrawable as background if shapeTheming is enabled, otherwise will |
| 250 | + // default to android:background declared in styles or layout. |
| 251 | + if (shapeThemingEnabled && materialShapeDrawable != null) { |
| 252 | + ViewCompat.setBackground(child, materialShapeDrawable); |
| 253 | + } |
| 254 | + |
224 | 255 | int savedTop = child.getTop(); |
225 | 256 | // First let the parent lay it out |
226 | 257 | parent.onLayoutChild(child, layoutDirection); |
@@ -708,11 +739,27 @@ void setStateInternal(@State int state) { |
708 | 739 | bottomSheet, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); |
709 | 740 | bottomSheet.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); |
710 | 741 |
|
| 742 | + updateDrawableOnStateChange(state); |
711 | 743 | if (callback != null) { |
712 | 744 | callback.onStateChanged(bottomSheet, state); |
713 | 745 | } |
714 | 746 | } |
715 | 747 |
|
| 748 | + private void updateDrawableOnStateChange(@State int state) { |
| 749 | + if (materialShapeDrawable != null) { |
| 750 | + if (state == STATE_EXPANDED && (parentHeight <= viewRef.get().getHeight())) { |
| 751 | + // If the bottomsheet is fully expanded, change ShapeAppearance to sharp corners to |
| 752 | + // indicate the bottomsheet has no more content to scroll. |
| 753 | + // Overriding of this style may be performed in the bottomsheet callback. |
| 754 | + materialShapeDrawable.getShapeAppearanceModel().setCornerRadius(0); |
| 755 | + materialShapeDrawable.invalidateSelf(); |
| 756 | + } |
| 757 | + if (state == STATE_COLLAPSED || state == STATE_DRAGGING) { |
| 758 | + materialShapeDrawable.setShapeAppearanceModel(shapeAppearanceModelDefault); |
| 759 | + } |
| 760 | + } |
| 761 | + } |
| 762 | + |
716 | 763 | private void calculateCollapsedOffset() { |
717 | 764 | if (fitToContents) { |
718 | 765 | collapsedOffset = Math.max(parentHeight - lastPeekHeight, fitToContentsOffset); |
@@ -758,6 +805,33 @@ View findScrollingChild(View view) { |
758 | 805 | return null; |
759 | 806 | } |
760 | 807 |
|
| 808 | + private void createMaterialShapeDrawable( |
| 809 | + Context context, AttributeSet attrs, boolean hasBackgroundTint) { |
| 810 | + this.createMaterialShapeDrawable(context, attrs, hasBackgroundTint, null); |
| 811 | + } |
| 812 | + |
| 813 | + private void createMaterialShapeDrawable( |
| 814 | + Context context, |
| 815 | + AttributeSet attrs, |
| 816 | + boolean hasBackgroundTint, |
| 817 | + @Nullable ColorStateList bottomSheetColor) { |
| 818 | + if (this.shapeThemingEnabled) { |
| 819 | + this.shapeAppearanceModelDefault = |
| 820 | + new ShapeAppearanceModel(context, attrs, R.attr.bottomSheetStyle, DEF_STYLE_RES); |
| 821 | + |
| 822 | + this.materialShapeDrawable = new MaterialShapeDrawable(shapeAppearanceModelDefault); |
| 823 | + |
| 824 | + if (hasBackgroundTint && bottomSheetColor != null) { |
| 825 | + materialShapeDrawable.setFillColor(bottomSheetColor); |
| 826 | + } else { |
| 827 | + // If the tint isn't set, use the theme default background color. |
| 828 | + TypedValue defaultColor = new TypedValue(); |
| 829 | + context.getTheme().resolveAttribute(android.R.attr.colorBackground, defaultColor, true); |
| 830 | + materialShapeDrawable.setTint(defaultColor.data); |
| 831 | + } |
| 832 | + } |
| 833 | + } |
| 834 | + |
761 | 835 | private float getYVelocity() { |
762 | 836 | if (velocityTracker == null) { |
763 | 837 | return 0; |
|
0 commit comments