Skip to content

Commit 86e8b00

Browse files
pekingmeraajkumars
authored andcommitted
[CollapsingToolbarLayout] Fixed shadow issues when transition between expanded and collapsed states.
Includes two main fixes: 1. Applied a workaround for alpha issue of shadow layer in Paint for API 31(+). - Issue: For API 31 and plus, Paint doesn't apply the correct alpha value to the shadow layer. When the shadow color is opaque (alpha=255), the alpha of shadow should use the alpha of the Paint. But it seems the inverse alpha (255-alpha) is applied to the shadow layer. - Solution: Added a workaround to set shadow color with a real-time alpha value, Paint still respects to the shadow color's alpha when it's not opaque. 2. Fixed rounding issue when blending shadow color for API 30(-). - Issue: The rounding of alpha value in removed blendColors() function is casting (round down). This causes the shadow color occasionally becomes non-opaque. Then Paint stops to apply the alpha value to it. - Solution: When blending colors, round values with Math.round() (round to nearest). Note: Fix (1) covers fix (2). But fix (2) is still worth to keep, if issue (1) is fixed in the framework in future and workaround is removed. Resolves: #2545 PiperOrigin-RevId: 427812326
1 parent d5856fd commit 86e8b00

File tree

1 file changed

+84
-42
lines changed

1 file changed

+84
-42
lines changed

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

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import androidx.core.view.GravityCompat;
5757
import androidx.core.view.ViewCompat;
5858
import com.google.android.material.animation.AnimationUtils;
59+
import com.google.android.material.color.MaterialColors;
5960
import com.google.android.material.internal.StaticLayoutBuilderCompat.StaticLayoutBuilderCompatException;
6061
import com.google.android.material.resources.CancelableFontCallback;
6162
import com.google.android.material.resources.CancelableFontCallback.ApplyFont;
@@ -136,6 +137,10 @@ public final class CollapsingTextHelper {
136137

137138
private float scale;
138139
private float currentTextSize;
140+
private float currentShadowRadius;
141+
private float currentShadowDx;
142+
private float currentShadowDy;
143+
private int currentShadowColor;
139144

140145
private int[] state;
141146

@@ -471,11 +476,11 @@ private boolean setCollapsedTypefaceInternal(Typeface typeface) {
471476
}
472477
if (collapsedTypefaceDefault != typeface) {
473478
collapsedTypefaceDefault = typeface;
474-
collapsedTypefaceBold = TypefaceUtils.maybeCopyWithFontWeightAdjustment(
475-
view.getContext().getResources().getConfiguration(),
476-
typeface);
477-
collapsedTypeface = collapsedTypefaceBold == null
478-
? collapsedTypefaceDefault : collapsedTypefaceBold;
479+
collapsedTypefaceBold =
480+
TypefaceUtils.maybeCopyWithFontWeightAdjustment(
481+
view.getContext().getResources().getConfiguration(), typeface);
482+
collapsedTypeface =
483+
collapsedTypefaceBold == null ? collapsedTypefaceDefault : collapsedTypefaceBold;
479484
return true;
480485
}
481486
return false;
@@ -490,11 +495,11 @@ private boolean setExpandedTypefaceInternal(Typeface typeface) {
490495
}
491496
if (expandedTypefaceDefault != typeface) {
492497
expandedTypefaceDefault = typeface;
493-
expandedTypefaceBold = TypefaceUtils.maybeCopyWithFontWeightAdjustment(
494-
view.getContext().getResources().getConfiguration(),
495-
typeface);
496-
expandedTypeface = expandedTypefaceBold == null
497-
? expandedTypefaceDefault : expandedTypefaceBold;
498+
expandedTypefaceBold =
499+
TypefaceUtils.maybeCopyWithFontWeightAdjustment(
500+
view.getContext().getResources().getConfiguration(), typeface);
501+
expandedTypeface =
502+
expandedTypefaceBold == null ? expandedTypefaceDefault : expandedTypefaceBold;
498503
return true;
499504
}
500505
return false;
@@ -511,20 +516,19 @@ public Typeface getExpandedTypeface() {
511516
public void maybeUpdateFontWeightAdjustment(@NonNull Configuration configuration) {
512517
if (VERSION.SDK_INT >= VERSION_CODES.S) {
513518
if (collapsedTypefaceDefault != null) {
514-
collapsedTypefaceBold = TypefaceUtils.maybeCopyWithFontWeightAdjustment(
515-
configuration,
516-
collapsedTypefaceDefault);
519+
collapsedTypefaceBold =
520+
TypefaceUtils.maybeCopyWithFontWeightAdjustment(
521+
configuration, collapsedTypefaceDefault);
517522
}
518523
if (expandedTypefaceDefault != null) {
519-
expandedTypefaceBold = TypefaceUtils.maybeCopyWithFontWeightAdjustment(
520-
configuration,
521-
expandedTypefaceDefault);
524+
expandedTypefaceBold =
525+
TypefaceUtils.maybeCopyWithFontWeightAdjustment(configuration, expandedTypefaceDefault);
522526
}
523-
collapsedTypeface = collapsedTypefaceBold != null
524-
? collapsedTypefaceBold : collapsedTypefaceDefault;
525-
expandedTypeface = expandedTypefaceBold != null
526-
? expandedTypefaceBold : expandedTypefaceDefault;
527-
recalculate(/* forceRecalculate= */true);
527+
collapsedTypeface =
528+
collapsedTypefaceBold != null ? collapsedTypefaceBold : collapsedTypefaceDefault;
529+
expandedTypeface =
530+
expandedTypefaceBold != null ? expandedTypefaceBold : expandedTypefaceDefault;
531+
recalculate(/* forceRecalculate= */ true);
528532
}
529533
}
530534

@@ -621,7 +625,7 @@ private void calculateOffsets(final float fraction) {
621625
// If the collapsed and expanded text colors are different, blend them based on the
622626
// fraction
623627
textPaint.setColor(
624-
blendColors(
628+
blendARGB(
625629
getCurrentExpandedTextColor(), getCurrentCollapsedTextColor(), textBlendFraction));
626630
} else {
627631
textPaint.setColor(getCurrentCollapsedTextColor());
@@ -640,12 +644,15 @@ private void calculateOffsets(final float fraction) {
640644
}
641645
}
642646

647+
// Calculates paint parameters for shadow layer.
648+
currentShadowRadius = lerp(expandedShadowRadius, collapsedShadowRadius, fraction, null);
649+
currentShadowDx = lerp(expandedShadowDx, collapsedShadowDx, fraction, null);
650+
currentShadowDy = lerp(expandedShadowDy, collapsedShadowDy, fraction, null);
651+
currentShadowColor =
652+
blendARGB(
653+
getCurrentColor(expandedShadowColor), getCurrentColor(collapsedShadowColor), fraction);
643654
textPaint.setShadowLayer(
644-
lerp(expandedShadowRadius, collapsedShadowRadius, fraction, null),
645-
lerp(expandedShadowDx, collapsedShadowDx, fraction, null),
646-
lerp(expandedShadowDy, collapsedShadowDy, fraction, null),
647-
blendColors(
648-
getCurrentColor(expandedShadowColor), getCurrentColor(collapsedShadowColor), fraction));
655+
currentShadowRadius, currentShadowDx, currentShadowDy, currentShadowColor);
649656

650657
if (fadeModeEnabled) {
651658
int originalAlpha = textPaint.getAlpha();
@@ -659,8 +666,7 @@ private void calculateOffsets(final float fraction) {
659666
ViewCompat.postInvalidateOnAnimation(view);
660667
}
661668

662-
private float calculateFadeModeTextAlpha(
663-
@FloatRange(from = 0.0, to = 1.0) float fraction) {
669+
private float calculateFadeModeTextAlpha(@FloatRange(from = 0.0, to = 1.0) float fraction) {
664670
if (fraction <= fadeModeThresholdFraction) {
665671
return AnimationUtils.lerp(
666672
/* startValue= */ 1,
@@ -870,10 +876,29 @@ private void drawMultilineTransition(@NonNull Canvas canvas, float currentExpand
870876
canvas.translate(currentExpandedX, y);
871877
// Expanded text
872878
textPaint.setAlpha((int) (expandedTextBlend * originalAlpha));
879+
// Workaround for API 31(+). Paint applies an inverse alpha of Paint object on the shadow layer
880+
// when collapsing mode is scale and shadow color is opaque. The workaround is to set the shadow
881+
// not opaque. Then Paint will respect to the color's alpha. Applying the shadow color for
882+
// expanded text.
883+
if (VERSION.SDK_INT >= VERSION_CODES.S) {
884+
textPaint.setShadowLayer(
885+
currentShadowRadius,
886+
currentShadowDx,
887+
currentShadowDy,
888+
MaterialColors.compositeARGBWithAlpha(currentShadowColor, textPaint.getAlpha()));
889+
}
873890
textLayout.draw(canvas);
874891

875892
// Collapsed text
876893
textPaint.setAlpha((int) (collapsedTextBlend * originalAlpha));
894+
// Workaround for API 31(+). Applying the shadow color for collapsed texct.
895+
if (VERSION.SDK_INT >= VERSION_CODES.S) {
896+
textPaint.setShadowLayer(
897+
currentShadowRadius,
898+
currentShadowDx,
899+
currentShadowDy,
900+
MaterialColors.compositeARGBWithAlpha(currentShadowColor, textPaint.getAlpha()));
901+
}
877902
int lineBaseline = textLayout.getLineBaseline(0);
878903
canvas.drawText(
879904
textToDrawCollapsed,
@@ -882,6 +907,13 @@ private void drawMultilineTransition(@NonNull Canvas canvas, float currentExpand
882907
/* x = */ 0,
883908
lineBaseline,
884909
textPaint);
910+
// Reverse workaround for API 31(+). Applying opaque shadow color after the expanded text and
911+
// the collapsed text are drawn.
912+
if (VERSION.SDK_INT >= VERSION_CODES.S) {
913+
textPaint.setShadowLayer(
914+
currentShadowRadius, currentShadowDx, currentShadowDy, currentShadowColor);
915+
}
916+
885917
if (!fadeModeEnabled) {
886918
// Remove ellipsis for Cross-section animation
887919
String tmp = textToDrawCollapsed.toString().trim();
@@ -991,9 +1023,10 @@ private void calculateUsingTextSize(final float fraction, boolean forceRecalcula
9911023
// the collapsed width
9921024
// Otherwise we'll just use the expanded width
9931025

994-
availableWidth = scaledDownWidth > collapsedWidth
995-
? min(collapsedWidth / textSizeRatio, expandedWidth)
996-
: expandedWidth;
1026+
availableWidth =
1027+
scaledDownWidth > collapsedWidth
1028+
? min(collapsedWidth / textSizeRatio, expandedWidth)
1029+
: expandedWidth;
9971030
}
9981031
}
9991032

@@ -1197,18 +1230,27 @@ public ColorStateList getCollapsedTextColor() {
11971230
}
11981231

11991232
/**
1200-
* Blend {@code color1} and {@code color2} using the given ratio.
1233+
* Blend between two ARGB colors using the given ratio.
1234+
*
1235+
* <p>A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend, 1.0 will
1236+
* result in {@code color2}.
12011237
*
1202-
* @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend,
1203-
* 1.0 will return {@code color2}.
1238+
* <p>This is different from the AndroidX implementation by rounding the blended channel values
1239+
* with {@link Math#round(float)}.
1240+
*
1241+
* @param color1 the first ARGB color
1242+
* @param color2 the second ARGB color
1243+
* @param ratio the blend ratio of {@code color1} to {@code color2}
12041244
*/
1205-
private static int blendColors(int color1, int color2, float ratio) {
1206-
final float inverseRatio = 1f - ratio;
1207-
float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);
1208-
float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);
1209-
float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);
1210-
float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);
1211-
return Color.argb((int) a, (int) r, (int) g, (int) b);
1245+
@ColorInt
1246+
private static int blendARGB(
1247+
@ColorInt int color1, @ColorInt int color2, @FloatRange(from = 0.0, to = 1.0) float ratio) {
1248+
final float inverseRatio = 1 - ratio;
1249+
float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio;
1250+
float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio;
1251+
float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio;
1252+
float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio;
1253+
return Color.argb(Math.round(a), Math.round(r), Math.round(g), Math.round(b));
12121254
}
12131255

12141256
private static float lerp(

0 commit comments

Comments
 (0)