Skip to content

Commit 1adc501

Browse files
pekingmekendrickumstattd
authored andcommitted
[AppBarLayout] Fixed the bug that setBackground will reset the internal MaterialShapeDrawable for lift-on-scroll feature.
PiperOrigin-RevId: 760744509
1 parent 9ae6b7f commit 1adc501

File tree

1 file changed

+57
-28
lines changed

1 file changed

+57
-28
lines changed

lib/java/com/google/android/material/appbar/AppBarLayout.java

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ public abstract void onUpdate(
225225
private boolean lifted;
226226

227227
private boolean liftOnScroll;
228+
@Nullable private ColorStateList liftOnScrollColor;
228229
@IdRes private int liftOnScrollTargetViewId;
229230
@Nullable private WeakReference<View> liftOnScrollTargetView;
230-
private final boolean hasLiftOnScrollColor;
231231
@Nullable private ValueAnimator liftOnScrollColorAnimator;
232232
@Nullable private AnimatorUpdateListener liftOnScrollColorUpdateListener;
233233
private final List<LiftOnScrollListener> liftOnScrollListeners = new ArrayList<>();
@@ -239,6 +239,7 @@ public abstract void onUpdate(
239239

240240
private int[] tmpStatesArray;
241241

242+
@ColorInt private int backgroundOriginalColor;
242243
@Nullable private Drawable statusBarForeground;
243244
@Nullable private Integer statusBarForegroundOriginalColor;
244245

@@ -272,25 +273,8 @@ public AppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int
272273
ThemeEnforcement.obtainStyledAttributes(
273274
context, attrs, R.styleable.AppBarLayout, defStyleAttr, DEF_STYLE_RES);
274275

275-
setBackground(a.getDrawable(R.styleable.AppBarLayout_android_background));
276-
277-
ColorStateList liftOnScrollColor =
276+
liftOnScrollColor =
278277
MaterialResources.getColorStateList(context, a, R.styleable.AppBarLayout_liftOnScrollColor);
279-
hasLiftOnScrollColor = liftOnScrollColor != null;
280-
281-
ColorStateList originalBackgroundColor = DrawableUtils.getColorStateListOrNull(getBackground());
282-
if (originalBackgroundColor != null) {
283-
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
284-
materialShapeDrawable.setFillColor(originalBackgroundColor);
285-
// If there is a lift on scroll color specified, we do not initialize the elevation overlay
286-
// and set the alpha to zero manually.
287-
if (liftOnScrollColor != null) {
288-
initializeLiftOnScrollWithColor(
289-
materialShapeDrawable, originalBackgroundColor, liftOnScrollColor);
290-
} else {
291-
initializeLiftOnScrollWithElevation(context, materialShapeDrawable);
292-
}
293-
}
294278

295279
liftOnScrollColorDuration =
296280
MotionUtils.resolveThemeDuration(
@@ -313,6 +297,9 @@ public AppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int
313297
this, a.getDimensionPixelSize(R.styleable.AppBarLayout_elevation, 0));
314298
}
315299

300+
// Set the background drawable last to ensure that the background is updated for lift on scroll.
301+
setBackground(a.getDrawable(R.styleable.AppBarLayout_android_background));
302+
316303
if (VERSION.SDK_INT >= VERSION_CODES.O) {
317304
// In O+, we have these values set in the style. Since there is no defStyleAttr for
318305
// AppBarLayout at the AppCompat level, check for these attributes here.
@@ -346,19 +333,49 @@ public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets)
346333
});
347334
}
348335

336+
private Drawable maybeCreateLiftOnScrollBackground(
337+
@NonNull Context context, @NonNull Drawable originalBackground) {
338+
MaterialShapeDrawable materialShapeDrawable =
339+
maybeConvertToMaterialShapeDrawable(originalBackground);
340+
if (materialShapeDrawable == null || materialShapeDrawable.getFillColor() == null) {
341+
return originalBackground;
342+
}
343+
backgroundOriginalColor = materialShapeDrawable.getFillColor().getDefaultColor();
344+
// If there is a lift on scroll color specified, we do not initialize the elevation overlay
345+
// and set the alpha to zero manually.
346+
if (liftOnScrollColor != null) {
347+
initializeLiftOnScrollWithColor(materialShapeDrawable, liftOnScrollColor);
348+
} else {
349+
initializeLiftOnScrollWithElevation(context, materialShapeDrawable);
350+
}
351+
return materialShapeDrawable;
352+
}
353+
354+
@Nullable
355+
private MaterialShapeDrawable maybeConvertToMaterialShapeDrawable(Drawable originalBackground) {
356+
if (originalBackground instanceof MaterialShapeDrawable) {
357+
return (MaterialShapeDrawable) originalBackground;
358+
}
359+
ColorStateList originalBackgroundColor =
360+
DrawableUtils.getColorStateListOrNull(originalBackground);
361+
if (originalBackgroundColor == null) {
362+
return null;
363+
}
364+
MaterialShapeDrawable materialShapeDrawable = new MaterialShapeDrawable();
365+
materialShapeDrawable.setFillColor(originalBackgroundColor);
366+
return materialShapeDrawable;
367+
}
368+
349369
private void initializeLiftOnScrollWithColor(
350370
MaterialShapeDrawable background,
351-
@NonNull ColorStateList originalBackgroundColor,
352371
@NonNull ColorStateList liftOnScrollColor) {
353372
Integer colorSurface = MaterialColors.getColorOrNull(getContext(), R.attr.colorSurface);
354373
liftOnScrollColorUpdateListener =
355374
valueAnimator -> {
356375
float liftProgress = (float) valueAnimator.getAnimatedValue();
357376
int mixedColor =
358377
MaterialColors.layer(
359-
originalBackgroundColor.getDefaultColor(),
360-
liftOnScrollColor.getDefaultColor(),
361-
liftProgress);
378+
backgroundOriginalColor, liftOnScrollColor.getDefaultColor(), liftProgress);
362379
background.setFillColor(ColorStateList.valueOf(mixedColor));
363380
if (statusBarForeground != null
364381
&& statusBarForegroundOriginalColor != null
@@ -380,8 +397,6 @@ private void initializeLiftOnScrollWithColor(
380397
}
381398
}
382399
};
383-
384-
setBackground(background);
385400
}
386401

387402
private void initializeLiftOnScrollWithElevation(
@@ -402,8 +417,6 @@ private void initializeLiftOnScrollWithElevation(
402417
elevation, background.getResolvedTintColor(), elevation / appBarElevation);
403418
}
404419
};
405-
406-
setBackground(background);
407420
}
408421

409422
/**
@@ -576,6 +589,11 @@ private Integer extractStatusBarForegroundColor() {
576589
return null;
577590
}
578591

592+
@Override
593+
public void setBackground(Drawable background) {
594+
super.setBackground(maybeCreateLiftOnScrollBackground(getContext(), background));
595+
}
596+
579597
@Override
580598
public void draw(@NonNull Canvas canvas) {
581599
super.draw(canvas);
@@ -1092,7 +1110,7 @@ boolean setLiftedState(boolean lifted, boolean force) {
10921110
this.lifted = lifted;
10931111
refreshDrawableState();
10941112
if (isLiftOnScrollCompatibleBackground()) {
1095-
if (hasLiftOnScrollColor) {
1113+
if (liftOnScrollColor != null) {
10961114
// Only start the liftOnScrollColor based animation because the elevation based
10971115
// animation will happen via the lifted drawable state change and state list animator.
10981116
startLiftOnScrollColorAnimation(lifted ? 0 : 1, lifted ? 1 : 0);
@@ -1164,6 +1182,17 @@ public void setLiftOnScrollTargetViewId(@IdRes int liftOnScrollTargetViewId) {
11641182
clearLiftOnScrollTargetView();
11651183
}
11661184

1185+
/** Sets the color of the {@link AppBarLayout} when it is fully lifted. */
1186+
public void setLiftOnScrollColor(@Nullable ColorStateList liftOnScrollColor) {
1187+
if (this.liftOnScrollColor != liftOnScrollColor) {
1188+
this.liftOnScrollColor = liftOnScrollColor;
1189+
// Force recreating the background drawable for the lift on scroll color change. The
1190+
// background could be potentially switched between liftOnScroll with color and liftOnScroll
1191+
// with elevation, based on whether the liftOnScroll color is null or not.
1192+
setBackground(getBackground());
1193+
}
1194+
}
1195+
11671196
/**
11681197
* Returns the id of the view that the {@link AppBarLayout} should use to determine whether it
11691198
* should be lifted.

0 commit comments

Comments
 (0)