diff --git a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java index 1760968c3c3..279945c6142 100644 --- a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java +++ b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java @@ -25,7 +25,6 @@ import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.RectF; -import androidx.annotation.ColorInt; import androidx.annotation.FloatRange; import androidx.annotation.IntRange; import androidx.annotation.NonNull; @@ -47,17 +46,17 @@ final class CircularDrawingDelegate extends DrawingDelegate 1) { // Breaks the arc at 0 degree for ESCAPE animation. - fillIndicator(canvas, paint, startFraction, 1, color, drawableAlpha); - fillIndicator(canvas, paint, 1, startFraction + arcFraction, color, drawableAlpha); + ActiveIndicator firstPart = new ActiveIndicator(); + firstPart.startFraction = startFraction; + firstPart.endFraction = 1f; + firstPart.color = color; + fillIndicator(canvas, paint, firstPart, drawableAlpha); + ActiveIndicator secondPart = new ActiveIndicator(); + secondPart.startFraction = 1f; + secondPart.endFraction = startFraction + arcFraction; + secondPart.color = color; + fillIndicator(canvas, paint, secondPart, drawableAlpha); return; } // Scale start and arc fraction for ESCAPE animation. @@ -206,13 +201,6 @@ void fillIndicator( } } - /** - * 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. - */ @Override void fillTrack( @NonNull Canvas canvas, diff --git a/lib/java/com/google/android/material/progressindicator/CircularIndeterminateAnimatorDelegate.java b/lib/java/com/google/android/material/progressindicator/CircularIndeterminateAnimatorDelegate.java index 8d4d5cc1fc1..3f56c997abd 100644 --- a/lib/java/com/google/android/material/progressindicator/CircularIndeterminateAnimatorDelegate.java +++ b/lib/java/com/google/android/material/progressindicator/CircularIndeterminateAnimatorDelegate.java @@ -25,6 +25,7 @@ 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.progressindicator.DrawingDelegate.ActiveIndicator; /** * This is the implementation class for drawing progress indicator in the circular indeterminate @@ -63,7 +64,7 @@ final class CircularIndeterminateAnimatorDelegate AnimationCallback animatorCompleteCallback = null; public CircularIndeterminateAnimatorDelegate(@NonNull CircularProgressIndicatorSpec spec) { - super(/*segmentCount=*/ 1); + super(/* indicatorCount= */ 1); baseSpec = spec; @@ -156,26 +157,31 @@ public void unregisterAnimatorsCompleteCallback() { /** Updates the segment position array based on current playtime. */ private void updateSegmentPositions(int playtime) { + ActiveIndicator activeIndicator = activeIndicators.get(0); // Adds constant rotation to segment positions. - segmentPositions[0] = CONSTANT_ROTATION_DEGREES * animationFraction + TAIL_DEGREES_OFFSET; - segmentPositions[1] = CONSTANT_ROTATION_DEGREES * animationFraction; + activeIndicator.startFraction = + CONSTANT_ROTATION_DEGREES * animationFraction + TAIL_DEGREES_OFFSET; + activeIndicator.endFraction = CONSTANT_ROTATION_DEGREES * animationFraction; // Adds cycle specific rotation to segment positions. for (int cycleIndex = 0; cycleIndex < TOTAL_CYCLES; cycleIndex++) { // While expanding. float fraction = getFractionInRange(playtime, DELAY_TO_EXPAND_IN_MS[cycleIndex], DURATION_TO_EXPAND_IN_MS); - segmentPositions[1] += interpolator.getInterpolation(fraction) * EXTRA_DEGREES_PER_CYCLE; + activeIndicator.endFraction += + interpolator.getInterpolation(fraction) * EXTRA_DEGREES_PER_CYCLE; // While collapsing. fraction = getFractionInRange( playtime, DELAY_TO_COLLAPSE_IN_MS[cycleIndex], DURATION_TO_COLLAPSE_IN_MS); - segmentPositions[0] += interpolator.getInterpolation(fraction) * EXTRA_DEGREES_PER_CYCLE; + activeIndicator.startFraction += + interpolator.getInterpolation(fraction) * EXTRA_DEGREES_PER_CYCLE; } // Closes the gap between head and tail for complete end. - segmentPositions[0] += (segmentPositions[1] - segmentPositions[0]) * completeEndFraction; + activeIndicator.startFraction += + (activeIndicator.endFraction - activeIndicator.startFraction) * completeEndFraction; - segmentPositions[0] /= 360; - segmentPositions[1] /= 360; + activeIndicator.startFraction = activeIndicator.startFraction / 360f; + activeIndicator.endFraction = activeIndicator.endFraction / 360f; } /** Updates the segment color array based on current playtime. */ @@ -190,7 +196,7 @@ private void maybeUpdateSegmentColors(int playtime) { int startColor = baseSpec.indicatorColors[startColorIndex]; int endColor = baseSpec.indicatorColors[endColorIndex]; float colorFraction = interpolator.getInterpolation(timeFraction); - segmentColors[0] = + activeIndicators.get(0).color = ArgbEvaluatorCompat.getInstance().evaluate(colorFraction, startColor, endColor); break; } @@ -200,7 +206,7 @@ private void maybeUpdateSegmentColors(int playtime) { @VisibleForTesting void resetPropertiesForNewStart() { indicatorColorIndexOffset = 0; - segmentColors[0] = baseSpec.indicatorColors[0]; + activeIndicators.get(0).color = baseSpec.indicatorColors[0]; completeEndFraction = 0f; } diff --git a/lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java b/lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java index 6252574a991..677dee0021b 100644 --- a/lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java +++ b/lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java @@ -18,7 +18,6 @@ import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Rect; import android.widget.ProgressBar; import androidx.annotation.NonNull; @@ -26,6 +25,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.google.android.material.progressindicator.DrawingDelegate.ActiveIndicator; /** This class draws the graphics for determinate mode. */ public final class DeterminateDrawable @@ -41,8 +41,9 @@ public final class DeterminateDrawable // Animation. private final SpringForce springForce; private final SpringAnimation springAnimation; - // Fraction of displayed indicator in the total width. - private float indicatorFraction; + // Active indicator for the progress. + private final ActiveIndicator activeIndicator; + private final ActiveIndicator partialTrack; // Whether to skip the spring animation on level change event. private boolean skipAnimationOnLevelChange = false; @@ -53,6 +54,9 @@ public final class DeterminateDrawable super(context, baseSpec); setDrawingDelegate(drawingDelegate); + activeIndicator = new ActiveIndicator(); + partialTrack = new ActiveIndicator(); + partialTrack.endFraction = 1f; springForce = new SpringForce(); @@ -230,28 +234,23 @@ public void draw(@NonNull Canvas canvas) { drawingDelegate.validateSpecAndAdjustCanvas( canvas, getBounds(), getGrowFraction(), isShowing(), isHiding()); - int indicatorColor = baseSpec.indicatorColors[0]; + activeIndicator.color = baseSpec.indicatorColors[0]; if (baseSpec.indicatorTrackGapSize > 0) { - int trackColor = baseSpec.trackColor; + partialTrack.color = baseSpec.trackColor; // Draws the full transparent track. - baseSpec.trackColor = Color.TRANSPARENT; - drawingDelegate.fillTrack(canvas, paint, getAlpha()); - baseSpec.trackColor = trackColor; + drawingDelegate.fillTrack(canvas, paint, /* drawableAlpha= */ 0); // 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, getAlpha()); + drawingDelegate.fillIndicator(canvas, paint, activeIndicator, getAlpha()); baseSpec.indicatorTrackGapSize = gapSize; - drawingDelegate.fillIndicator( - canvas, paint, getIndicatorFraction(), 1f, trackColor, getAlpha()); + drawingDelegate.fillIndicator(canvas, paint, partialTrack, getAlpha()); } else { drawingDelegate.fillTrack(canvas, paint, getAlpha()); - drawingDelegate.fillIndicator( - canvas, paint, 0f, getIndicatorFraction(), indicatorColor, getAlpha()); + drawingDelegate.fillIndicator(canvas, paint, activeIndicator, getAlpha()); } canvas.restore(); } @@ -259,11 +258,12 @@ public void draw(@NonNull Canvas canvas) { // ******************* Getters and setters ******************* private float getIndicatorFraction() { - return indicatorFraction; + return activeIndicator.endFraction; } private void setIndicatorFraction(float indicatorFraction) { - this.indicatorFraction = indicatorFraction; + activeIndicator.endFraction = indicatorFraction; + partialTrack.startFraction = indicatorFraction; invalidateSelf(); } diff --git a/lib/java/com/google/android/material/progressindicator/DrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/DrawingDelegate.java index 20b285392d8..3429312d97d 100644 --- a/lib/java/com/google/android/material/progressindicator/DrawingDelegate.java +++ b/lib/java/com/google/android/material/progressindicator/DrawingDelegate.java @@ -64,22 +64,17 @@ abstract void adjustCanvas( boolean isHiding); /** - * Fills a part of the track with the designated indicator color. The filling part is defined with - * two fractions normalized to [0, 1] representing the start and the end of the track. + * Fills a part of the track as an active indicator. * * @param canvas Canvas to draw. * @param paint Paint used to draw. - * @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 activeIndicator The ActiveIndicator object of the current active indicator being drawn. * @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, + @NonNull ActiveIndicator activeIndicator, @IntRange(from = 0, to = 255) int drawableAlpha); /** @@ -103,4 +98,17 @@ void validateSpecAndAdjustCanvas( spec.validateSpec(); adjustCanvas(canvas, bounds, trackThicknessFraction, isShowing, isHiding); } + + protected static class ActiveIndicator { + // The fraction [0, 1] of the start position on the full track. + @FloatRange(from = 0.0, to = 1.0) + float startFraction; + + // The fraction [0, 1] of the end position on the full track. + @FloatRange(from = 0.0, to = 1.0) + float endFraction; + + // The color of the indicator without applying the drawable's alpha. + @ColorInt int color; + } } diff --git a/lib/java/com/google/android/material/progressindicator/IndeterminateAnimatorDelegate.java b/lib/java/com/google/android/material/progressindicator/IndeterminateAnimatorDelegate.java index 123c7f52689..41aafb6928b 100644 --- a/lib/java/com/google/android/material/progressindicator/IndeterminateAnimatorDelegate.java +++ b/lib/java/com/google/android/material/progressindicator/IndeterminateAnimatorDelegate.java @@ -20,26 +20,27 @@ import android.graphics.drawable.Drawable; import androidx.annotation.NonNull; import androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback; +import com.google.android.material.progressindicator.DrawingDelegate.ActiveIndicator; +import java.util.ArrayList; +import java.util.List; /** A delegate abstract class for animating properties used in drawing the graphics. */ abstract class IndeterminateAnimatorDelegate { // The drawable associated with this delegate. protected IndeterminateDrawable drawable; - // A float array of numbers in [0, 1] representing the positions of ends of each segment on the - // track. - protected final float[] segmentPositions; - // An integer array of displayed colors used for each indicator segment defined by every two - // segment positions. - protected final int[] segmentColors; + + protected final List activeIndicators; /** * This constructor should be overridden with other necessary actions, e.g. instantiating the * animator. */ - protected IndeterminateAnimatorDelegate(int segmentCount) { - this.segmentPositions = new float[segmentCount * 2]; - this.segmentColors = new int[segmentCount]; + protected IndeterminateAnimatorDelegate(int indicatorCount) { + activeIndicators = new ArrayList<>(); + for (int i = 0; i < indicatorCount; i++) { + activeIndicators.add(new ActiveIndicator()); + } } /** Registers the drawable associated to this delegate. */ @@ -62,7 +63,7 @@ protected float getFractionInRange(int playtime, int start, int duration) { /** * Invalidates the spec values used by the animator delegate. When the spec values are changed in - * indicator class, values assigned to animators or segments don't get updated until they are + * indicator class, values assigned to animators or indicators don't get updated until they are * explicitly reset. Call this to apply the changes immediately. */ public abstract void invalidateSpecValues(); diff --git a/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java b/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java index ae1cf001f59..4a28e29dcc7 100644 --- a/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java +++ b/lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java @@ -36,6 +36,7 @@ import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.DrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; +import com.google.android.material.progressindicator.DrawingDelegate.ActiveIndicator; /** This class draws the graphics for indeterminate mode. */ public final class IndeterminateDrawable @@ -224,48 +225,40 @@ public void draw(@NonNull Canvas canvas) { drawingDelegate.fillTrack(canvas, paint, getAlpha()); } - for (int segmentIndex = 0; - segmentIndex < animatorDelegate.segmentColors.length; - segmentIndex++) { + for (int indicatorIndex = 0; + indicatorIndex < animatorDelegate.activeIndicators.size(); + indicatorIndex++) { // Draws the actual indicators. drawingDelegate.fillIndicator( - canvas, - paint, - animatorDelegate.segmentPositions[2 * segmentIndex], - animatorDelegate.segmentPositions[2 * segmentIndex + 1], - animatorDelegate.segmentColors[segmentIndex], - getAlpha()); + canvas, paint, animatorDelegate.activeIndicators.get(indicatorIndex), getAlpha()); if (drawingDelegate instanceof LinearDrawingDelegate && baseSpec.indicatorTrackGapSize > 0) { // Draws the track using fake indicators around the current indicator. - drawTrackIndicators(canvas, segmentIndex); + drawTrackIndicators(canvas, indicatorIndex); } } canvas.restore(); } - private void drawTrackIndicators(@NonNull Canvas canvas, int segmentIndex) { - float previousSegmentEndPosition = - segmentIndex == 0 ? 0f : animatorDelegate.segmentPositions[2 * segmentIndex - 1]; + private void drawTrackIndicators(@NonNull Canvas canvas, int indicatorIndex) { + float previousIndicatorEndFraction = + indicatorIndex == 0 + ? 0f + : animatorDelegate.activeIndicators.get(indicatorIndex - 1).endFraction; // Draws the fake indicators as the track to the left of the current indicator. - drawingDelegate.fillIndicator( - canvas, - paint, - previousSegmentEndPosition, - animatorDelegate.segmentPositions[2 * segmentIndex], - baseSpec.trackColor, - getAlpha()); - if (segmentIndex == animatorDelegate.segmentColors.length - 1) { + ActiveIndicator track = new ActiveIndicator(); + track.startFraction = previousIndicatorEndFraction; + track.endFraction = animatorDelegate.activeIndicators.get(indicatorIndex).startFraction; + track.color = baseSpec.trackColor; + drawingDelegate.fillIndicator(canvas, paint, track, getAlpha()); + if (indicatorIndex == animatorDelegate.activeIndicators.size() - 1) { // Draws the fake indicator as the track to the right of the last indicator. - drawingDelegate.fillIndicator( - canvas, - paint, - animatorDelegate.segmentPositions[2 * segmentIndex + 1], - 1f, - baseSpec.trackColor, - getAlpha()); + track.startFraction = animatorDelegate.activeIndicators.get(indicatorIndex).endFraction; + track.endFraction = 1f; + track.color = baseSpec.trackColor; + drawingDelegate.fillIndicator(canvas, paint, track, getAlpha()); } } diff --git a/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java index c99f16e0384..6f06a86d8c6 100644 --- a/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java +++ b/lib/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java @@ -28,7 +28,6 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; -import androidx.annotation.ColorInt; import androidx.annotation.FloatRange; import androidx.annotation.IntRange; import androidx.annotation.NonNull; @@ -49,17 +48,17 @@ final class LinearDrawingDelegate extends DrawingDelegate