Skip to content

Commit 3ce7c2b

Browse files
committed
[SearchBar] Update predictive back device corner logic to handle each corner independently to fix issue where bottom corners are incorrectly rounded after canceling back gesture
PiperOrigin-RevId: 677788624
1 parent 9a8ca4d commit 3ce7c2b

File tree

3 files changed

+130
-42
lines changed

3 files changed

+130
-42
lines changed

lib/java/com/google/android/material/internal/ClippableRoundedCornerLayout.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
public class ClippableRoundedCornerLayout extends FrameLayout {
3939

4040
@Nullable private Path path;
41-
private float cornerRadius;
41+
@NonNull private float[] cornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
4242

4343
public ClippableRoundedCornerLayout(@NonNull Context context) {
4444
super(context);
@@ -65,36 +65,37 @@ protected void dispatchDraw(Canvas canvas) {
6565
canvas.restoreToCount(save);
6666
}
6767

68-
public void resetClipBoundsAndCornerRadius() {
68+
public void resetClipBoundsAndCornerRadii() {
6969
path = null;
70-
cornerRadius = 0f;
70+
cornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
7171
invalidate();
7272
}
7373

74-
public float getCornerRadius() {
75-
return cornerRadius;
74+
@NonNull
75+
public float[] getCornerRadii() {
76+
return cornerRadii;
7677
}
7778

78-
public void updateCornerRadius(float cornerRadius) {
79-
updateClipBoundsAndCornerRadius(getLeft(), getTop(), getRight(), getBottom(), cornerRadius);
79+
public void updateCornerRadii(@NonNull float[] cornerRadii) {
80+
updateClipBoundsAndCornerRadii(getLeft(), getTop(), getRight(), getBottom(), cornerRadii);
8081
}
8182

82-
public void updateClipBoundsAndCornerRadius(@NonNull Rect rect, float cornerRadius) {
83-
updateClipBoundsAndCornerRadius(rect.left, rect.top, rect.right, rect.bottom, cornerRadius);
83+
public void updateClipBoundsAndCornerRadii(@NonNull Rect rect, @NonNull float[] cornerRadii) {
84+
updateClipBoundsAndCornerRadii(rect.left, rect.top, rect.right, rect.bottom, cornerRadii);
8485
}
8586

86-
public void updateClipBoundsAndCornerRadius(
87-
float left, float top, float right, float bottom, float cornerRadius) {
88-
updateClipBoundsAndCornerRadius(new RectF(left, top, right, bottom), cornerRadius);
87+
public void updateClipBoundsAndCornerRadii(
88+
float left, float top, float right, float bottom, @NonNull float[] cornerRadii) {
89+
updateClipBoundsAndCornerRadii(new RectF(left, top, right, bottom), cornerRadii);
8990
}
9091

91-
public void updateClipBoundsAndCornerRadius(@NonNull RectF rectF, float cornerRadius) {
92+
public void updateClipBoundsAndCornerRadii(@NonNull RectF rectF, @NonNull float[] cornerRadii) {
9293
if (path == null) {
9394
path = new Path();
9495
}
95-
this.cornerRadius = cornerRadius;
96+
this.cornerRadii = cornerRadii;
9697
path.reset();
97-
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
98+
path.addRoundRect(rectF, cornerRadii, Path.Direction.CW);
9899
path.close();
99100
invalidate();
100101
}

lib/java/com/google/android/material/motion/MaterialMainContainerBackHelper.java

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import android.graphics.Rect;
3232
import android.os.Build.VERSION;
3333
import android.os.Build.VERSION_CODES;
34+
import android.util.DisplayMetrics;
3435
import android.view.RoundedCorner;
3536
import android.view.View;
3637
import android.view.WindowInsets;
@@ -61,7 +62,7 @@ public class MaterialMainContainerBackHelper extends MaterialBackAnimationHelper
6162
private float initialTouchY;
6263
@Nullable private Rect initialHideToClipBounds;
6364
@Nullable private Rect initialHideFromClipBounds;
64-
@Nullable private Integer expandedCornerSize;
65+
@Nullable private float[] expandedCornerRadii;
6566

6667
public MaterialMainContainerBackHelper(@NonNull View view) {
6768
super(view);
@@ -141,7 +142,8 @@ public void updateBackProgress(
141142
view.setTranslationY(translationY);
142143
if (view instanceof ClippableRoundedCornerLayout) {
143144
((ClippableRoundedCornerLayout) view)
144-
.updateCornerRadius(lerp(getExpandedCornerSize(), collapsedCornerSize, progress));
145+
.updateCornerRadii(
146+
lerpCornerRadii(getExpandedCornerRadii(), collapsedCornerSize, progress));
145147
}
146148
}
147149

@@ -198,41 +200,92 @@ public void onAnimationEnd(Animator animation) {
198200
private ValueAnimator createCornerAnimator(
199201
ClippableRoundedCornerLayout clippableRoundedCornerLayout) {
200202
ValueAnimator cornerAnimator =
201-
ValueAnimator.ofFloat(
202-
clippableRoundedCornerLayout.getCornerRadius(), getExpandedCornerSize());
203+
ValueAnimator.ofObject(
204+
(fraction, startValue, endValue) ->
205+
lerpCornerRadii((float[]) startValue, (float[]) endValue, fraction),
206+
clippableRoundedCornerLayout.getCornerRadii(),
207+
getExpandedCornerRadii());
203208
cornerAnimator.addUpdateListener(
204209
animation ->
205-
clippableRoundedCornerLayout.updateCornerRadius((Float) animation.getAnimatedValue()));
210+
clippableRoundedCornerLayout.updateCornerRadii((float[]) animation.getAnimatedValue()));
206211
return cornerAnimator;
207212
}
208213

209-
public int getExpandedCornerSize() {
210-
if (expandedCornerSize == null) {
211-
expandedCornerSize = isAtTopOfScreen() ? getMaxDeviceCornerRadius() : 0;
214+
private static float[] lerpCornerRadii(float[] startValue, float[] endValue, float fraction) {
215+
return new float[] {
216+
lerp(startValue[0], endValue[0], fraction),
217+
lerp(startValue[1], endValue[1], fraction),
218+
lerp(startValue[2], endValue[2], fraction),
219+
lerp(startValue[3], endValue[3], fraction),
220+
lerp(startValue[4], endValue[4], fraction),
221+
lerp(startValue[5], endValue[5], fraction),
222+
lerp(startValue[6], endValue[6], fraction),
223+
lerp(startValue[7], endValue[7], fraction)
224+
};
225+
}
226+
227+
private static float[] lerpCornerRadii(float[] startValue, float endValue, float fraction) {
228+
return new float[] {
229+
lerp(startValue[0], endValue, fraction),
230+
lerp(startValue[1], endValue, fraction),
231+
lerp(startValue[2], endValue, fraction),
232+
lerp(startValue[3], endValue, fraction),
233+
lerp(startValue[4], endValue, fraction),
234+
lerp(startValue[5], endValue, fraction),
235+
lerp(startValue[6], endValue, fraction),
236+
lerp(startValue[7], endValue, fraction)
237+
};
238+
}
239+
240+
@NonNull
241+
public float[] getExpandedCornerRadii() {
242+
if (expandedCornerRadii == null) {
243+
expandedCornerRadii = calculateExpandedCornerRadii();
212244
}
213-
return expandedCornerSize;
245+
return expandedCornerRadii;
214246
}
215247

216-
private boolean isAtTopOfScreen() {
217-
int[] location = new int[2];
218-
view.getLocationOnScreen(location);
219-
return location[1] == 0;
248+
public void clearExpandedCornerRadii() {
249+
expandedCornerRadii = null;
220250
}
221251

222-
private int getMaxDeviceCornerRadius() {
252+
private float[] calculateExpandedCornerRadii() {
223253
if (VERSION.SDK_INT >= VERSION_CODES.S) {
224254
final WindowInsets insets = view.getRootWindowInsets();
225255
if (insets != null) {
226-
return max(
227-
max(
228-
getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_LEFT),
229-
getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_RIGHT)),
230-
max(
231-
getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_LEFT),
232-
getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_RIGHT)));
256+
DisplayMetrics displayMetrics = view.getResources().getDisplayMetrics();
257+
int screenWidth = displayMetrics.widthPixels;
258+
int screenHeight = displayMetrics.heightPixels;
259+
260+
int[] location = new int[2];
261+
view.getLocationOnScreen(location);
262+
int x = location[0];
263+
int y = location[1];
264+
265+
int width = view.getWidth();
266+
int height = view.getHeight();
267+
268+
int topLeft =
269+
x == 0 && y == 0 ? getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_LEFT) : 0;
270+
int topRight =
271+
x + width >= screenWidth && y == 0
272+
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_RIGHT)
273+
: 0;
274+
int bottomRight =
275+
x + width >= screenWidth && y + height >= screenHeight
276+
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_RIGHT)
277+
: 0;
278+
int bottomLeft =
279+
x == 0 && y + height >= screenHeight
280+
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_LEFT)
281+
: 0;
282+
283+
return new float[] {
284+
topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft
285+
};
233286
}
234287
}
235-
return 0;
288+
return new float[] {0, 0, 0, 0, 0, 0, 0, 0};
236289
}
237290

238291
@RequiresApi(VERSION_CODES.S)

lib/java/com/google/android/material/search/SearchViewAnimationHelper.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,13 @@ public void onAnimationEnd(Animator animation) {
299299
setContentViewsAlpha(show ? 1 : 0);
300300
// After expanding or collapsing, we should reset the clip bounds so it can react to the
301301
// screen or layout changes. Otherwise it will result in wrong clipping on the layout.
302-
rootView.resetClipBoundsAndCornerRadius();
302+
rootView.resetClipBoundsAndCornerRadii();
303+
304+
// After collapsing, we should reset the expanded corner radii in case the search view
305+
// is shown in a different location the next time.
306+
if (!show) {
307+
backHelper.clearExpandedCornerRadii();
308+
}
303309
}
304310
});
305311
return animatorSet;
@@ -347,22 +353,50 @@ private Animator getRootViewAnimator(boolean show) {
347353
Rect clipBounds = new Rect(fromClipBounds);
348354

349355
float fromCornerRadius = searchBar.getCornerSize();
350-
float toCornerRadius = max(rootView.getCornerRadius(), backHelper.getExpandedCornerSize());
356+
float[] toCornerRadius =
357+
maxCornerRadii(rootView.getCornerRadii(), backHelper.getExpandedCornerRadii());
351358

352359
ValueAnimator animator =
353360
ValueAnimator.ofObject(new RectEvaluator(clipBounds), fromClipBounds, toClipBounds);
354361
animator.addUpdateListener(
355362
valueAnimator -> {
356-
float cornerRadius =
357-
lerp(fromCornerRadius, toCornerRadius, valueAnimator.getAnimatedFraction());
358-
rootView.updateClipBoundsAndCornerRadius(clipBounds, cornerRadius);
363+
float[] cornerRadii =
364+
lerpCornerRadii(
365+
fromCornerRadius, toCornerRadius, valueAnimator.getAnimatedFraction());
366+
rootView.updateClipBoundsAndCornerRadii(clipBounds, cornerRadii);
359367
});
360368
animator.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS);
361369
animator.setInterpolator(
362370
ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR));
363371
return animator;
364372
}
365373

374+
private static float[] maxCornerRadii(float[] startValue, float[] endValue) {
375+
return new float[] {
376+
max(startValue[0], endValue[0]),
377+
max(startValue[1], endValue[1]),
378+
max(startValue[2], endValue[2]),
379+
max(startValue[3], endValue[3]),
380+
max(startValue[4], endValue[4]),
381+
max(startValue[5], endValue[5]),
382+
max(startValue[6], endValue[6]),
383+
max(startValue[7], endValue[7])
384+
};
385+
}
386+
387+
private static float[] lerpCornerRadii(float startValue, float[] endValue, float fraction) {
388+
return new float[] {
389+
lerp(startValue, endValue[0], fraction),
390+
lerp(startValue, endValue[1], fraction),
391+
lerp(startValue, endValue[2], fraction),
392+
lerp(startValue, endValue[3], fraction),
393+
lerp(startValue, endValue[4], fraction),
394+
lerp(startValue, endValue[5], fraction),
395+
lerp(startValue, endValue[6], fraction),
396+
lerp(startValue, endValue[7], fraction)
397+
};
398+
}
399+
366400
private Animator getClearButtonAnimator(boolean show) {
367401
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
368402
animator.setDuration(

0 commit comments

Comments
 (0)