Skip to content

Commit

Permalink
[ProgressIndicator] Updated to use the same drawing delegate object b…
Browse files Browse the repository at this point in the history
…etween determinate drawable and indeterminate drawable to prevent inconsistent drawings when specs update.

PiperOrigin-RevId: 595468907
  • Loading branch information
pekingme committed Jan 3, 2024
1 parent 1ef42e2 commit 52b4845
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.graphics.RectF;
import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import com.google.android.material.color.MaterialColors;

Expand Down Expand Up @@ -60,14 +61,19 @@ public int getPreferredHeight() {
* current track thickness.
*
* @param canvas Canvas to draw.
* @param bounds Bounds that the drawable is supposed to be drawn within
* @param trackThicknessFraction A fraction representing how much portion of the track thickness
* should be used in the drawing.
* should be used in the drawing
* @param isShowing Whether the drawable is currently animating to show
* @param isHiding Whether the drawable is currently animating to hide
*/
@Override
public void adjustCanvas(
@NonNull Canvas canvas,
@NonNull Rect bounds,
@FloatRange(from = 0.0, to = 1.0) float trackThicknessFraction) {
@FloatRange(from = 0.0, to = 1.0) float trackThicknessFraction,
boolean isShowing,
boolean isHiding) {
// Scales the actual drawing by the ratio of the given bounds to the preferred size.
float scaleX = (float) bounds.width() / getPreferredWidth();
float scaleY = (float) bounds.height() / getPreferredHeight();
Expand Down Expand Up @@ -96,18 +102,14 @@ public void adjustCanvas(
displayedTrackThickness = spec.trackThickness * trackThicknessFraction;
displayedCornerRadius = spec.trackCornerRadius * trackThicknessFraction;
adjustedRadius = (spec.indicatorSize - spec.trackThickness) / 2f;
if ((drawable.isShowing()
&& spec.showAnimationBehavior == CircularProgressIndicator.SHOW_INWARD)
|| (drawable.isHiding()
&& spec.hideAnimationBehavior == CircularProgressIndicator.HIDE_OUTWARD)) {
if ((isShowing && spec.showAnimationBehavior == CircularProgressIndicator.SHOW_INWARD)
|| (isHiding && spec.hideAnimationBehavior == CircularProgressIndicator.HIDE_OUTWARD)) {
// Increases the radius by half of the full thickness, then reduces it half way of the
// displayed thickness to match the outer edges of the displayed indicator and the full
// indicator.
adjustedRadius += (1 - trackThicknessFraction) * spec.trackThickness / 2;
} else if ((drawable.isShowing()
&& spec.showAnimationBehavior == CircularProgressIndicator.SHOW_OUTWARD)
|| (drawable.isHiding()
&& spec.hideAnimationBehavior == CircularProgressIndicator.HIDE_INWARD)) {
} else if ((isShowing && spec.showAnimationBehavior == CircularProgressIndicator.SHOW_OUTWARD)
|| (isHiding && spec.hideAnimationBehavior == CircularProgressIndicator.HIDE_INWARD)) {
// Decreases the radius by half of the full thickness, then raises it half way of the
// displayed thickness to match the inner edges of the displayed indicator and the full
// indicator.
Expand All @@ -125,18 +127,21 @@ public void adjustCanvas(
* @param startFraction A fraction representing where to start the drawing along the track.
* @param endFraction A fraction representing where to end the drawing along the track.
* @param color The color used to draw the indicator.
* @param drawableAlpha The alpha [0, 255] from the caller drawable.
*/
@Override
void fillIndicator(
@NonNull Canvas canvas,
@NonNull Paint paint,
@FloatRange(from = 0.0, to = 1.0) float startFraction,
@FloatRange(from = 0.0, to = 1.0) float endFraction,
@ColorInt int color) {
@ColorInt int color,
@IntRange(from = 0, to = 255) int drawableAlpha) {
// No need to draw if startFraction and endFraction are same.
if (startFraction == endFraction) {
return;
}
color = MaterialColors.compositeARGBWithAlpha(color, drawableAlpha);

// Sets up the paint.
paint.setStyle(Style.STROKE);
Expand Down Expand Up @@ -182,10 +187,14 @@ void fillIndicator(
*
* @param canvas Canvas to draw.
* @param paint Paint used to draw.
* @param drawableAlpha The alpha [0, 255] from the caller drawable.
*/
@Override
void fillTrack(@NonNull Canvas canvas, @NonNull Paint paint) {
int trackColor = MaterialColors.compositeARGBWithAlpha(spec.trackColor, drawable.getAlpha());
void fillTrack(
@NonNull Canvas canvas,
@NonNull Paint paint,
@IntRange(from = 0, to = 255) int drawableAlpha) {
int trackColor = MaterialColors.compositeARGBWithAlpha(spec.trackColor, drawableAlpha);

// Sets up the paint.
paint.setStyle(Style.STROKE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback;
import com.google.android.material.animation.ArgbEvaluatorCompat;
import com.google.android.material.color.MaterialColors;

/**
* This is the implementation class for drawing progress indicator in the circular indeterminate
Expand Down Expand Up @@ -188,12 +187,8 @@ private void maybeUpdateSegmentColors(int playtime) {
int startColorIndex =
(cycleIndex + indicatorColorIndexOffset) % baseSpec.indicatorColors.length;
int endColorIndex = (startColorIndex + 1) % baseSpec.indicatorColors.length;
int startColor =
MaterialColors.compositeARGBWithAlpha(
baseSpec.indicatorColors[startColorIndex], drawable.getAlpha());
int endColor =
MaterialColors.compositeARGBWithAlpha(
baseSpec.indicatorColors[endColorIndex], drawable.getAlpha());
int startColor = baseSpec.indicatorColors[startColorIndex];
int endColor = baseSpec.indicatorColors[endColorIndex];
float colorFraction = interpolator.getInterpolation(timeFraction);
segmentColors[0] =
ArgbEvaluatorCompat.getInstance().evaluate(colorFraction, startColor, endColor);
Expand All @@ -205,8 +200,7 @@ private void maybeUpdateSegmentColors(int playtime) {
@VisibleForTesting
void resetPropertiesForNewStart() {
indicatorColorIndexOffset = 0;
segmentColors[0] =
MaterialColors.compositeARGBWithAlpha(baseSpec.indicatorColors[0], drawable.getAlpha());
segmentColors[0] = baseSpec.indicatorColors[0];
completeEndFraction = 0f;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ CircularProgressIndicatorSpec createSpec(@NonNull Context context, @NonNull Attr
// ******************** Initialization **********************

private void initializeDrawables() {
setIndeterminateDrawable(IndeterminateDrawable.createCircularDrawable(getContext(), spec));
setProgressDrawable(DeterminateDrawable.createCircularDrawable(getContext(), spec));
CircularDrawingDelegate drawingDelegate = new CircularDrawingDelegate(spec);
setIndeterminateDrawable(
IndeterminateDrawable.createCircularDrawable(getContext(), spec, drawingDelegate));
setProgressDrawable(
DeterminateDrawable.createCircularDrawable(getContext(), spec, drawingDelegate));
}

// **************** Getters and setters ****************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.google.android.material.color.MaterialColors;

/** This class draws the graphics for determinate mode. */
public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
Expand Down Expand Up @@ -76,8 +75,23 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
@NonNull
public static DeterminateDrawable<LinearProgressIndicatorSpec> createLinearDrawable(
@NonNull Context context, @NonNull LinearProgressIndicatorSpec spec) {
return new DeterminateDrawable<>(
context, /* baseSpec= */ spec, new LinearDrawingDelegate(spec));
return createLinearDrawable(context, spec, new LinearDrawingDelegate(spec));
}

/**
* Creates an instance of {@link DeterminateDrawable} for {@link LinearProgressIndicator} with
* {@link LinearProgressIndicatorSpec}.
*
* @param context The current context.
* @param spec The spec for the linear indicator.
* @param drawingDelegate The LinearDrawingDelegate object.
*/
@NonNull
static DeterminateDrawable<LinearProgressIndicatorSpec> createLinearDrawable(
@NonNull Context context,
@NonNull LinearProgressIndicatorSpec spec,
@NonNull LinearDrawingDelegate drawingDelegate) {
return new DeterminateDrawable<>(context, /* baseSpec= */ spec, drawingDelegate);
}

/**
Expand All @@ -90,8 +104,23 @@ public static DeterminateDrawable<LinearProgressIndicatorSpec> createLinearDrawa
@NonNull
public static DeterminateDrawable<CircularProgressIndicatorSpec> createCircularDrawable(
@NonNull Context context, @NonNull CircularProgressIndicatorSpec spec) {
return new DeterminateDrawable<>(
context, /* baseSpec= */ spec, new CircularDrawingDelegate(spec));
return createCircularDrawable(context, spec, new CircularDrawingDelegate(spec));
}

/**
* Creates an instance of {@link DeterminateDrawable} for {@link CircularProgressIndicator} with
* {@link CircularProgressIndicatorSpec}.
*
* @param context The current context.
* @param spec The spec for the circular indicator.
* @param drawingDelegate The CircularDrawingDelegate object.
*/
@NonNull
static DeterminateDrawable<CircularProgressIndicatorSpec> createCircularDrawable(
@NonNull Context context,
@NonNull CircularProgressIndicatorSpec spec,
@NonNull CircularDrawingDelegate drawingDelegate) {
return new DeterminateDrawable<>(context, /* baseSpec= */ spec, drawingDelegate);
}

public void addSpringAnimationEndListener(
Expand Down Expand Up @@ -198,28 +227,31 @@ public void draw(@NonNull Canvas canvas) {
}

canvas.save();
drawingDelegate.validateSpecAndAdjustCanvas(canvas, getBounds(), getGrowFraction());
drawingDelegate.validateSpecAndAdjustCanvas(
canvas, getBounds(), getGrowFraction(), isShowing(), isHiding());

int indicatorColor =
MaterialColors.compositeARGBWithAlpha(baseSpec.indicatorColors[0], getAlpha());
int indicatorColor = baseSpec.indicatorColors[0];
if (baseSpec.indicatorTrackGapSize > 0) {
int trackColor = MaterialColors.compositeARGBWithAlpha(baseSpec.trackColor, getAlpha());
int trackColor = baseSpec.trackColor;
// Draws the full transparent track.
baseSpec.trackColor = Color.TRANSPARENT;
drawingDelegate.fillTrack(canvas, paint);
drawingDelegate.fillTrack(canvas, paint, getAlpha());
baseSpec.trackColor = trackColor;
// Draws the indicator and track.
int gapSize = baseSpec.indicatorTrackGapSize;
// TODO: workaround to maintain pixel-perfect compatibility with drawing logic
// not using indicatorTrackGapSize.
// See https://github.com/material-components/material-components-android/commit/0ce6ae4.
baseSpec.indicatorTrackGapSize = 0;
drawingDelegate.fillIndicator(canvas, paint, 0f, getIndicatorFraction(), indicatorColor);
drawingDelegate.fillIndicator(
canvas, paint, 0f, getIndicatorFraction(), indicatorColor, getAlpha());
baseSpec.indicatorTrackGapSize = gapSize;
drawingDelegate.fillIndicator(canvas, paint, getIndicatorFraction(), 1f, trackColor);
drawingDelegate.fillIndicator(
canvas, paint, getIndicatorFraction(), 1f, trackColor, getAlpha());
} else {
drawingDelegate.fillTrack(canvas, paint);
drawingDelegate.fillIndicator(canvas, paint, 0f, getIndicatorFraction(), indicatorColor);
drawingDelegate.fillTrack(canvas, paint, getAlpha());
drawingDelegate.fillIndicator(
canvas, paint, 0f, getIndicatorFraction(), indicatorColor, getAlpha());
}
canvas.restore();
}
Expand All @@ -242,7 +274,6 @@ DrawingDelegate<S> getDrawingDelegate() {

void setDrawingDelegate(@NonNull DrawingDelegate<S> drawingDelegate) {
this.drawingDelegate = drawingDelegate;
drawingDelegate.registerDrawable(this);
}

// ******************* Properties *******************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.graphics.Rect;
import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;

/** A delegate abstract class for drawing the graphics in different drawable classes. */
Expand All @@ -32,8 +33,6 @@ public DrawingDelegate(S spec) {
this.spec = spec;
}

protected DrawableWithAnimatedVisibilityChange drawable;

/**
* Returns the preferred width, in pixels, of the drawable based on the drawing type. Returns a
* negative value if it depends on the {@link android.view.View}.
Expand All @@ -54,11 +53,15 @@ public DrawingDelegate(S spec) {
* @param bounds Bounds that the drawable is supposed to be drawn within
* @param trackThicknessFraction A fraction representing how much portion of the track thickness
* should be used in the drawing
* @param isShowing Whether the drawable is currently animating to show
* @param isHiding Whether the drawable is currently animating to hide
*/
abstract void adjustCanvas(
@NonNull Canvas canvas,
@NonNull Rect bounds,
@FloatRange(from = 0.0, to = 1.0) float trackThicknessFraction);
@FloatRange(from = -1.0, to = 1.0) float trackThicknessFraction,
boolean isShowing,
boolean isHiding);

/**
* Fills a part of the track with the designated indicator color. The filling part is defined with
Expand All @@ -69,31 +72,35 @@ abstract void adjustCanvas(
* @param startFraction A fraction representing where to start the drawing along the track.
* @param endFraction A fraction representing where to end the drawing along the track.
* @param color The color used to draw the indicator.
* @param drawableAlpha The alpha [0, 255] from the caller drawable.
*/
abstract void fillIndicator(
@NonNull Canvas canvas,
@NonNull Paint paint,
@FloatRange(from = 0.0, to = 1.0) float startFraction,
@FloatRange(from = 0.0, to = 1.0) float endFraction,
@ColorInt int color);
@ColorInt int color,
@IntRange(from = 0, to = 255) int drawableAlpha);

/**
* Fills the whole track with track color.
*
* @param canvas Canvas to draw.
* @param paint Paint used to draw.
* @param drawableAlpha The alpha [0, 255] from the caller drawable.
*/
abstract void fillTrack(@NonNull Canvas canvas, @NonNull Paint paint);

protected void registerDrawable(@NonNull DrawableWithAnimatedVisibilityChange drawable) {
this.drawable = drawable;
}
abstract void fillTrack(
@NonNull Canvas canvas,
@NonNull Paint paint,
@IntRange(from = 0, to = 255) int drawableAlpha);

void validateSpecAndAdjustCanvas(
@NonNull Canvas canvas,
@NonNull Rect bounds,
@FloatRange(from = 0.0, to = 1.0) float trackThicknessFraction) {
@FloatRange(from = 0.0, to = 1.0) float trackThicknessFraction,
boolean isShowing,
boolean isHiding) {
spec.validateSpec();
adjustCanvas(canvas, bounds, trackThicknessFraction);
adjustCanvas(canvas, bounds, trackThicknessFraction, isShowing, isHiding);
}
}

0 comments on commit 52b4845

Please sign in to comment.