Skip to content

Commit c831ecc

Browse files
cketchamldjcmu
authored andcommitted
Add support for percentages to ShapeAppearanceModel
PiperOrigin-RevId: 272946740 (cherry picked from commit a130a0c)
1 parent 5ff7b35 commit c831ecc

File tree

11 files changed

+131
-136
lines changed

11 files changed

+131
-136
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@
7171
</style>
7272

7373
<style name="ShapeAppearanceOverlay.MaterialComponents.Chip" parent="">
74-
<!-- Replace with 50% -->
75-
<item name="cornerSize">16dp</item>
74+
<item name="cornerSize">50%</item>
7675
</style>
7776

7877
<!-- Style for Chips that appear in text fields as a span.

lib/java/com/google/android/material/floatingactionbutton/BaseMotionStrategy.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,6 @@ AnimatorSet createAnimator(@NonNull MotionSpec spec) {
134134
animators.add(spec.getAnimator("height", fab, ExtendedFloatingActionButton.HEIGHT));
135135
}
136136

137-
if (spec.hasPropertyValues("cornerRadius") && fab.isUsingPillCorner()) {
138-
animators
139-
.add(spec.getAnimator("cornerRadius", fab, ExtendedFloatingActionButton.CORNER_RADIUS));
140-
}
141-
142137
AnimatorSet set = new AnimatorSet();
143138
AnimatorSetCompat.playTogether(set, animators);
144139
return set;

lib/java/com/google/android/material/floatingactionbutton/ExtendedFloatingActionButton.java

Lines changed: 13 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import android.content.Context;
2727
import android.content.res.TypedArray;
2828
import android.graphics.Rect;
29+
import android.graphics.RectF;
2930
import androidx.annotation.AnimatorRes;
3031
import androidx.annotation.NonNull;
3132
import androidx.annotation.Nullable;
@@ -48,7 +49,7 @@
4849
import com.google.android.material.bottomsheet.BottomSheetBehavior;
4950
import com.google.android.material.internal.DescendantOffsetUtils;
5051
import com.google.android.material.internal.ThemeEnforcement;
51-
import com.google.android.material.shape.MaterialShapeDrawable;
52+
import com.google.android.material.shape.CornerSize;
5253
import com.google.android.material.shape.ShapeAppearanceModel;
5354
import java.util.List;
5455

@@ -89,7 +90,6 @@ public class ExtendedFloatingActionButton extends MaterialButton implements Atta
8990
@NonNull private final Behavior<ExtendedFloatingActionButton> behavior;
9091

9192
private boolean isExtended = true;
92-
private boolean isUsingPillCorner = true;
9393

9494
/**
9595
* Callback to be invoked when the visibility or the state of an ExtendedFloatingActionButton
@@ -200,7 +200,17 @@ public int getHeight() {
200200

201201
ShapeAppearanceModel shapeAppearanceModel =
202202
ShapeAppearanceModel.builder(
203-
context, attrs, defStyleAttr, DEF_STYLE_RES, ShapeAppearanceModel.PILL)
203+
context,
204+
attrs,
205+
defStyleAttr,
206+
DEF_STYLE_RES,
207+
// TODO(b/121352029): Use ShapeAppearanceModel.PILL once this bug is fixed.
208+
new CornerSize() {
209+
@Override
210+
public float getCornerSize(@NonNull RectF bounds) {
211+
return getAdjustedRadius((int) bounds.height());
212+
}
213+
})
204214
.build();
205215
setShapeAppearanceModel(shapeAppearanceModel);
206216
}
@@ -215,45 +225,12 @@ protected void onAttachedToWindow() {
215225
}
216226
}
217227

218-
@Override
219-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
220-
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
221-
222-
if (isUsingPillCorner) {
223-
setShapeAppearanceModel(createPillCornerShapeAppearance());
224-
}
225-
}
226-
227228
@NonNull
228229
@Override
229230
public Behavior<ExtendedFloatingActionButton> getBehavior() {
230231
return behavior;
231232
}
232233

233-
@Override
234-
public void setShapeAppearanceModel(@NonNull ShapeAppearanceModel shapeAppearanceModel) {
235-
if (shapeAppearanceModel.isUsingPillCorner()) {
236-
isUsingPillCorner = true;
237-
shapeAppearanceModel = createPillCornerShapeAppearance();
238-
}
239-
super.setShapeAppearanceModel(shapeAppearanceModel);
240-
}
241-
242-
@NonNull
243-
private ShapeAppearanceModel createPillCornerShapeAppearance() {
244-
return getShapeAppearanceModel().withCornerRadius(getAdjustedRadius(getMeasuredHeight()));
245-
}
246-
247-
@Override
248-
public void setCornerRadius(int cornerRadius) {
249-
isUsingPillCorner = cornerRadius == ShapeAppearanceModel.PILL;
250-
if (isUsingPillCorner) {
251-
cornerRadius = getAdjustedRadius(getMeasuredHeight());
252-
} else if (cornerRadius < 0) {
253-
cornerRadius = 0;
254-
}
255-
super.setCornerRadius(cornerRadius);
256-
}
257234

258235
/**
259236
* Extends or shrinks the fab depending on the value of {@param extended}.
@@ -554,10 +531,6 @@ public void setShrinkMotionSpecResource(@AnimatorRes int id) {
554531
setShrinkMotionSpec(MotionSpec.createFromResource(getContext(), id));
555532
}
556533

557-
boolean isUsingPillCorner() {
558-
return isUsingPillCorner;
559-
}
560-
561534
private void performMotion(
562535
@NonNull final MotionStrategy strategy, @Nullable final OnChangedCallback callback) {
563536
if (strategy.shouldCancel()) {
@@ -667,26 +640,6 @@ public Float get(@NonNull View object) {
667640
}
668641
};
669642

670-
/**
671-
* A Property wrapper around the <code>cornerRadius</code> functionality handled by the {@link
672-
* ExtendedFloatingActionButton#setCornerRadius(int)} and {@link
673-
* ExtendedFloatingActionButton#getCornerRadius()} methods.
674-
*/
675-
static final Property<View, Float> CORNER_RADIUS =
676-
new Property<View, Float>(Float.class, "cornerRadius") {
677-
@Override
678-
public void set(@NonNull View object, @NonNull Float value) {
679-
ExtendedFloatingActionButton efab = ((ExtendedFloatingActionButton) object);
680-
efab.setShapeAppearanceModel(
681-
efab.getShapeAppearanceModel().withCornerRadius(value.intValue()));
682-
}
683-
684-
@Override
685-
public Float get(@NonNull View object) {
686-
return ((MaterialShapeDrawable) object.getBackground()).getTopRightCornerResolvedSize();
687-
}
688-
};
689-
690643
/**
691644
* Returns an adjusted radius value that corrects any rounding errors.
692645
*

lib/java/com/google/android/material/floatingactionbutton/FloatingActionButton.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
import com.google.android.material.internal.VisibilityAwareImageButton;
7777
import com.google.android.material.resources.MaterialResources;
7878
import com.google.android.material.shadow.ShadowViewDelegate;
79-
import com.google.android.material.shape.AbsoluteCornerSize;
8079
import com.google.android.material.shape.ShapeAppearanceModel;
8180
import com.google.android.material.shape.Shapeable;
8281
import com.google.android.material.stateful.ExtendableSavedState;
@@ -245,7 +244,6 @@ public FloatingActionButton(
245244
context, attrs, defStyleAttr, DEF_STYLE_RES, ShapeAppearanceModel.PILL)
246245
.build();
247246

248-
boolean usingDefaultCorner = isUsingDefaultCorner(shapeAppearance);
249247
boolean ensureMinTouchTargetSize = a
250248
.getBoolean(R.styleable.FloatingActionButton_ensureMinTouchTargetSize, false);
251249

@@ -256,7 +254,7 @@ public FloatingActionButton(
256254

257255
expandableWidgetHelper = new ExpandableWidgetHelper(this);
258256

259-
getImpl().setShapeAppearance(shapeAppearance, usingDefaultCorner);
257+
getImpl().setShapeAppearance(shapeAppearance);
260258
getImpl()
261259
.initializeBackgroundDrawable(backgroundTint, backgroundTintMode, rippleColor, borderWidth);
262260
getImpl().setMinTouchTargetSize(minTouchTargetSize);
@@ -522,7 +520,7 @@ public void setImageDrawable(@Nullable Drawable drawable) {
522520
/** Sets the {@link ShapeAppearanceModel} for this {@link FloatingActionButton}. */
523521
@Override
524522
public void setShapeAppearanceModel(@NonNull ShapeAppearanceModel shapeAppearance) {
525-
getImpl().setShapeAppearance(shapeAppearance, isUsingDefaultCorner(shapeAppearance));
523+
getImpl().setShapeAppearance(shapeAppearance);
526524
}
527525

528526
/** Returns the {@link ShapeAppearanceModel} for this {@link FloatingActionButton}. */
@@ -691,7 +689,6 @@ public void setSize(@Size int size) {
691689
customSize = NO_CUSTOM_SIZE;
692690
if (size != this.size) {
693691
this.size = size;
694-
getImpl().updateSize();
695692
requestLayout();
696693
}
697694
}
@@ -752,7 +749,6 @@ public void setCustomSize(@Px int size) {
752749

753750
if (size != customSize) {
754751
customSize = size;
755-
getImpl().updateSize();
756752
requestLayout();
757753
}
758754
}
@@ -1362,11 +1358,6 @@ public void removeTransformationCallback(
13621358
getImpl().removeTransformationCallback(new TransformationCallbackWrapper(listener));
13631359
}
13641360

1365-
private boolean isUsingDefaultCorner(@NonNull ShapeAppearanceModel shapeAppearance) {
1366-
return ((AbsoluteCornerSize) shapeAppearance.getTopRightCornerSize()).getCornerSize()
1367-
== ShapeAppearanceModel.PILL;
1368-
}
1369-
13701361
class TransformationCallbackWrapper<T extends FloatingActionButton>
13711362
implements InternalTransformationCallback {
13721363

lib/java/com/google/android/material/floatingactionbutton/FloatingActionButtonImpl.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ class FloatingActionButtonImpl {
8686
@Nullable BorderDrawable borderDrawable;
8787
@Nullable Drawable contentBackground;
8888

89-
boolean usingDefaultCorner;
9089
boolean ensureMinTouchTargetSize;
9190
boolean shadowPaddingEnabled = true;
9291
float elevation;
@@ -305,14 +304,8 @@ private void calculateImageMatrixFromScale(float scale, @NonNull Matrix matrix)
305304
}
306305
}
307306

308-
final void setShapeAppearance(
309-
@NonNull ShapeAppearanceModel shapeAppearance, boolean usingDefaultCorner) {
310-
if (usingDefaultCorner) {
311-
shapeAppearance = shapeAppearance.withCornerRadius(view.getSizeDimension() / 2);
312-
}
313-
307+
final void setShapeAppearance(@NonNull ShapeAppearanceModel shapeAppearance) {
314308
this.shapeAppearance = shapeAppearance;
315-
this.usingDefaultCorner = usingDefaultCorner;
316309
if (shapeDrawable != null) {
317310
shapeDrawable.setShapeAppearanceModel(shapeAppearance);
318311
}
@@ -645,16 +638,6 @@ void onCompatShadowChanged() {
645638
// Ignore pre-v21
646639
}
647640

648-
void updateSize() {
649-
if (!usingDefaultCorner || shapeDrawable == null || shapeAppearance == null) {
650-
// Leave shape appearance as is.
651-
return;
652-
}
653-
654-
setShapeAppearance(
655-
shapeAppearance.withCornerRadius(view.getSizeDimension() / 2f), usingDefaultCorner);
656-
}
657-
658641
final void updatePadding() {
659642
Rect rect = tmpRect;
660643
getPadding(rect);
@@ -736,9 +719,6 @@ public boolean onPreDraw() {
736719

737720
MaterialShapeDrawable createShapeDrawable() {
738721
ShapeAppearanceModel shapeAppearance = checkNotNull(this.shapeAppearance);
739-
if (usingDefaultCorner) {
740-
shapeAppearance = shapeAppearance.withCornerRadius(view.getSizeDimension() / 2f);
741-
}
742722
return new MaterialShapeDrawable(shapeAppearance);
743723
}
744724

lib/java/com/google/android/material/floatingactionbutton/FloatingActionButtonImplLollipop.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,6 @@ BorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTi
229229
@Override
230230
MaterialShapeDrawable createShapeDrawable() {
231231
ShapeAppearanceModel shapeAppearance = checkNotNull(this.shapeAppearance);
232-
if (usingDefaultCorner) {
233-
shapeAppearance = shapeAppearance.withCornerRadius(view.getSizeDimension() / 2f);
234-
}
235232
return new AlwaysStatefulMaterialShapeDrawable(shapeAppearance);
236233
}
237234

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@
5151
</style>
5252

5353
<style name="ShapeAppearanceOverlay.MaterialComponents.FloatingActionButton" parent="">
54-
<!-- use @null to change to 50% programmatically. -->
55-
<item name="cornerSize">@null</item>
54+
<item name="cornerSize">50%</item>
5655
</style>
5756

5857
<style name="Widget.MaterialComponents.ExtendedFloatingActionButton" parent="Widget.MaterialComponents.Button">
@@ -82,10 +81,15 @@
8281
<item name="iconTint">@color/mtrl_extended_fab_text_color_selector</item>
8382
<item name="rippleColor">@color/mtrl_extended_fab_ripple_color</item>
8483
<item name="shapeAppearanceOverlay">
85-
@style/ShapeAppearanceOverlay.MaterialComponents.FloatingActionButton
84+
@style/ShapeAppearanceOverlay.MaterialComponents.ExtendedFloatingActionButton
8685
</item>
8786
</style>
8887

88+
<style name="ShapeAppearanceOverlay.MaterialComponents.ExtendedFloatingActionButton" parent="">
89+
<!-- TODO(b/121352029): ExtendedFloatingActionButton adds 1px to the height -->
90+
<item name="cornerSize">@null</item>
91+
</style>
92+
8993
<style name="Widget.MaterialComponents.ExtendedFloatingActionButton.Icon" parent="Widget.MaterialComponents.ExtendedFloatingActionButton">
9094
<item name="android:gravity">start|center_vertical</item>
9195
<item name="android:paddingStart" tools:ignore="NewApi">

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import com.google.android.material.color.MaterialColors;
6161
import com.google.android.material.elevation.ElevationOverlayProvider;
6262
import com.google.android.material.shadow.ShadowRenderer;
63+
import com.google.android.material.shape.ShapeAppearanceModel.CornerSizeUnaryOperator;
6364
import java.lang.annotation.Retention;
6465
import java.lang.annotation.RetentionPolicy;
6566

@@ -1096,7 +1097,20 @@ protected final void calculatePathForSize(@NonNull RectF bounds, @NonNull Path p
10961097
private void calculateStrokePath() {
10971098
// Adjust corner radius in order to draw the stroke so that the corners of the background are
10981099
// drawn on top of the edges.
1099-
strokeShapeAppearance = getShapeAppearanceModel().withAdjustedCorners(-getStrokeInsetLength());
1100+
strokeShapeAppearance =
1101+
getShapeAppearanceModel()
1102+
.withTransformedCornerSizes(
1103+
new CornerSizeUnaryOperator() {
1104+
@NonNull
1105+
@Override
1106+
public CornerSize apply(@NonNull CornerSize cornerSize) {
1107+
// Don't adjust for relative corners they will change by themselves when the
1108+
// bounds change.
1109+
return cornerSize instanceof RelativeCornerSize
1110+
? cornerSize
1111+
: new AdjustedCornerSize(-getStrokeInsetLength(), cornerSize);
1112+
}
1113+
});
11001114

11011115
pathProvider.calculatePath(
11021116
strokeShapeAppearance,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2019 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+
17+
package com.google.android.material.shape;
18+
19+
import android.graphics.RectF;
20+
import androidx.annotation.NonNull;
21+
import java.util.Arrays;
22+
23+
/**
24+
* A {@link CornerSize} that takes a percent and computes the size used based on the height of the
25+
* shape.
26+
*/
27+
public final class RelativeCornerSize implements CornerSize {
28+
29+
private final float percent;
30+
31+
public RelativeCornerSize(float percent) {
32+
this.percent = percent;
33+
}
34+
35+
/** Returns the relative percent used for this {@link CornerSize} */
36+
public float getRelativePercent() {
37+
return percent;
38+
}
39+
40+
@Override
41+
public float getCornerSize(@NonNull RectF bounds) {
42+
return percent * bounds.height();
43+
}
44+
45+
@Override
46+
public boolean equals(Object o) {
47+
if (this == o) {
48+
return true;
49+
}
50+
if (!(o instanceof RelativeCornerSize)) {
51+
return false;
52+
}
53+
RelativeCornerSize that = (RelativeCornerSize) o;
54+
return percent == that.percent;
55+
}
56+
57+
@Override
58+
public int hashCode() {
59+
Object[] hashedFields = {percent};
60+
return Arrays.hashCode(hashedFields);
61+
}
62+
}

0 commit comments

Comments
 (0)