Skip to content

Commit

Permalink
[NTC][ProgressIndicator] Internal changes
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 581401427
  • Loading branch information
paulfthomas authored and dsn5ft committed Nov 13, 2023
1 parent 5e5eee0 commit bc59873
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 54 deletions.
30 changes: 16 additions & 14 deletions docs/components/ProgressIndicator.md
Expand Up @@ -289,25 +289,27 @@ A progress indicator consists of a track and an indicator.
The following attributes are shared between linear and circular progress
indicators:

Element | Attribute | Related method(s) | Default value
----------------------------- | --------------------------- | --------------------------------------------------------- | -------------
**Track thickness** | `app:trackThickness` | `setTrackThickness`</br>`getTrackThickness` | `4dp`
**Indicator color** | `app:indicatorColor` | `setIndicatorColor`</br>`getIndicatorColor` | `colorPrimary`
**Track color** | `app:trackColor` | `setTrackColor`</br>`getTrackColor` | `colorSurfaceContainerHighest` (linear)</br>`@android:color/transparent` (circular)
**Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius`</br>`getTrackCornerRadius` | `0dp`
**Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior`</br>`getShowAnimationBehavior` | `none`
**Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior`</br>`getHideAnimationBehavior` | `none`
**Delay (in ms) to show** | `app:showDelay` | N/A | 0
**Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0
| Element | Attribute | Related method(s) | Default value |
|-------------------------------|-----------------------------|-----------------------------------------------------------|-------------------------------------------------------------------------------------|
| **Track thickness** | `app:trackThickness` | `setTrackThickness`</br>`getTrackThickness` | `4dp` |
| **Indicator color** | `app:indicatorColor` | `setIndicatorColor`</br>`getIndicatorColor` | `colorPrimary` |
| **Track color** | `app:trackColor` | `setTrackColor`</br>`getTrackColor` | `colorSurfaceContainerHighest` (linear)</br>`@android:color/transparent` (circular) |
| **Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius`</br>`getTrackCornerRadius` | `0dp` |
| **Indicator track gap size** | `app:indicatorTrackGapSize` | `setIndicatorTrackGapSize`</br>`getIndicatorTrackGapSize` | `4dp` |
| **Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior`</br>`getShowAnimationBehavior` | `none` |
| **Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior`</br>`getHideAnimationBehavior` | `none` |
| **Delay (in ms) to show** | `app:showDelay` | N/A | 0 |
| **Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0 |

#### Linear type specific attributes

Linear type progress indicators also have the following attributes:

Element | Attribute | Related method(s) | Default value
-------------------------------- | -------------------------------- | ------------------------------------------------------------------- | -------------
**Indeterminate animation type** | `app:indeterminateAnimationType` | `setIndeterminateAnimationType`</br>`getIndeterminateAnimationType` | `disjoint`
**Indicator direction** | `app:indicatorDirectionLinear` | `setIndicatorDirection`</br>`getIndicatorDirection` | `leftToRight`
| Element | Attribute | Related method(s) | Default value |
|----------------------------------|----------------------------------|---------------------------------------------------------------------|---------------|
| **Indeterminate animation type** | `app:indeterminateAnimationType` | `setIndeterminateAnimationType`</br>`getIndeterminateAnimationType` | `disjoint` |
| **Indicator direction** | `app:indicatorDirectionLinear` | `setIndicatorDirection`</br>`getIndicatorDirection` | `leftToRight` |
| **Track stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize`</br>`getTrackStopIndicatorSize` | `4dp` |

#### Circular type specific attributes

Expand Down
Expand Up @@ -76,6 +76,7 @@ public abstract class BaseProgressIndicator<S extends BaseProgressIndicatorSpec>
public static final int HIDE_NONE = 0;
public static final int HIDE_OUTWARD = 1;
public static final int HIDE_INWARD = 2;
public static final int HIDE_ESCAPE = 3;

static final int DEF_STYLE_RES = R.style.Widget_MaterialComponents_ProgressIndicator;

Expand Down Expand Up @@ -636,6 +637,34 @@ public void setTrackCornerRadius(@Px int trackCornerRadius) {
}
}

/**
* Returns the size of the gap between the indicator and track in pixels.
*
* @see #setIndicatorTrackGapSize(int)
* @attr ref
* com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_indicatorTrackGapSize
*/
@Px
public int getIndicatorTrackGapSize() {
return spec.indicatorTrackGapSize;
}

/**
* Sets the size of the gap between the indicator and track in pixels.
*
* @param indicatorTrackGapSize The new gap size in pixels.
* @see #getIndicatorTrackGapSize()
* @attr ref
* com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_indicatorTrackGapSize
*/
public void setIndicatorTrackGapSize(@Px int indicatorTrackGapSize) {
if (spec.indicatorTrackGapSize != indicatorTrackGapSize) {
spec.indicatorTrackGapSize = indicatorTrackGapSize;
spec.validateSpec();
invalidate();
}
}

/**
* Returns the show animation behavior used in this progress indicator.
*
Expand Down Expand Up @@ -843,7 +872,7 @@ public void onAnimationEnd(Drawable drawable) {

/** @hide */
@RestrictTo(Scope.LIBRARY_GROUP)
@IntDef({HIDE_NONE, HIDE_OUTWARD, HIDE_INWARD})
@IntDef({HIDE_NONE, HIDE_OUTWARD, HIDE_INWARD, HIDE_ESCAPE})
@Retention(RetentionPolicy.SOURCE)
public @interface HideAnimationBehavior {}
}
Expand Up @@ -26,6 +26,7 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.CallSuper;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -57,8 +58,8 @@ public abstract class BaseProgressIndicatorSpec {
@NonNull public int[] indicatorColors = new int[0];

/**
* The color used in the track. If not defined, it will be set to the indicatorColors and
* apply the first disable alpha value from the theme.
* The color used in the track. If not defined, it will be set to the indicatorColors and apply
* the first disable alpha value from the theme.
*/
@ColorInt public int trackColor;

Expand All @@ -68,6 +69,9 @@ public abstract class BaseProgressIndicatorSpec {
/** The animation behavior to hide the indicator and track. */
@HideAnimationBehavior public int hideAnimationBehavior;

/** The size of the gap between the indicator and the rest of the track. */
@Px public int indicatorTrackGapSize;

/**
* Instantiates BaseProgressIndicatorSpec.
*
Expand Down Expand Up @@ -103,6 +107,7 @@ protected BaseProgressIndicatorSpec(
a.getInt(
R.styleable.BaseProgressIndicator_hideAnimationBehavior,
BaseProgressIndicator.HIDE_NONE);
indicatorTrackGapSize = a.getDimensionPixelSize(R.styleable.BaseProgressIndicator_indicatorTrackGapSize, 0);

loadIndicatorColors(context, a);
loadTrackColor(context, a);
Expand Down Expand Up @@ -178,5 +183,11 @@ public boolean isHideAnimationEnabled() {
return hideAnimationBehavior != BaseProgressIndicator.HIDE_NONE;
}

abstract void validateSpec();
@CallSuper
void validateSpec() {
if (indicatorTrackGapSize < 0) {
// Throws an exception if trying to use a negative gap size.
throw new IllegalArgumentException("indicatorTrackGapSize must be >= 0.");
}
}
}
Expand Up @@ -15,6 +15,8 @@
*/
package com.google.android.material.progressindicator;

import static java.lang.Math.min;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
Expand Down Expand Up @@ -150,12 +152,24 @@ void fillIndicator(
? (endFraction - startFraction) * 360 * arcDirectionFactor
: (1 + endFraction - startFraction) * 360 * arcDirectionFactor;

// Draws the gaps if needed.
if (spec.indicatorTrackGapSize > 0) {
float gapSize =
min(spec.getIndicatorTrackGapSizeDegree(), Math.abs(startDegree)) * arcDirectionFactor;
// No need to draw if the indicator is shorter than gap.
if (Math.abs(arcDegree) <= Math.abs(gapSize) * 2) {
return;
}
startDegree += gapSize;
arcDegree -= gapSize * 2;
}

// Draws the indicator arc without rounded corners.
RectF arcBound = new RectF(-adjustedRadius, -adjustedRadius, adjustedRadius, adjustedRadius);
canvas.drawArc(arcBound, startDegree, arcDegree, false, paint);

// Draws rounded corners if needed.
if (displayedCornerRadius > 0 && Math.abs(arcDegree) < 360) {
if (displayedCornerRadius > 0 && Math.abs(arcDegree) > 0 && Math.abs(arcDegree) < 360) {
paint.setStyle(Style.FILL);
drawRoundedEnd(canvas, paint, displayedTrackThickness, displayedCornerRadius, startDegree);
drawRoundedEnd(
Expand Down
Expand Up @@ -105,6 +105,13 @@ public CircularProgressIndicatorSpec(
validateSpec();
}

@Override
void validateSpec() {}
/** The size of the gap between the indicator and the rest of the track in degrees. */
int getIndicatorTrackGapSizeDegree() {
if (indicatorTrackGapSize == 0) {
return 0;
}
int diameter = indicatorSize - (indicatorInset * 2) - trackThickness;
double perimeter = Math.PI * diameter;
return (int) Math.round(360 / (perimeter / (indicatorTrackGapSize + trackCornerRadius)));
}
}
Expand Up @@ -18,7 +18,9 @@

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
Expand Down Expand Up @@ -74,7 +76,8 @@ 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 new DeterminateDrawable<>(
context, /* baseSpec= */ spec, new LinearDrawingDelegate(spec));
}

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

public void addSpringAnimationEndListener(
Expand Down Expand Up @@ -197,12 +200,27 @@ public void draw(@NonNull Canvas canvas) {
canvas.save();
drawingDelegate.validateSpecAndAdjustCanvas(canvas, getBounds(), getGrowFraction());

// Draws the track.
drawingDelegate.fillTrack(canvas, paint);
// Draws the indicator.
int indicatorColor =
MaterialColors.compositeARGBWithAlpha(baseSpec.indicatorColors[0], getAlpha());
drawingDelegate.fillIndicator(canvas, paint, 0f, getIndicatorFraction(), indicatorColor);
if (baseSpec.indicatorTrackGapSize > 0) {
int trackColor = MaterialColors.compositeARGBWithAlpha(baseSpec.trackColor, getAlpha());
// Draws the full transparent track.
baseSpec.trackColor = Color.TRANSPARENT;
drawingDelegate.fillTrack(canvas, paint);
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);
baseSpec.indicatorTrackGapSize = gapSize;
drawingDelegate.fillIndicator(canvas, paint, getIndicatorFraction(), 1f, trackColor);
} else {
drawingDelegate.fillTrack(canvas, paint);
drawingDelegate.fillIndicator(canvas, paint, 0f, getIndicatorFraction(), indicatorColor);
}
canvas.restore();
}

Expand Down
Expand Up @@ -15,13 +15,17 @@
*/
package com.google.android.material.progressindicator;

import static com.google.android.material.progressindicator.LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.annotation.NonNull;
import com.google.android.material.color.MaterialColors;

/** This class draws the graphics for indeterminate mode. */
public final class IndeterminateDrawable<S extends BaseProgressIndicatorSpec>
Expand Down Expand Up @@ -55,10 +59,9 @@ public static IndeterminateDrawable<LinearProgressIndicatorSpec> createLinearDra
@NonNull Context context, @NonNull LinearProgressIndicatorSpec spec) {
return new IndeterminateDrawable<>(
context,
/*baseSpec=*/ spec,
/* baseSpec= */ spec,
new LinearDrawingDelegate(spec),
spec.indeterminateAnimationType
== LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS
spec.indeterminateAnimationType == INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS
? new LinearIndeterminateContiguousAnimatorDelegate(spec)
: new LinearIndeterminateDisjointAnimatorDelegate(context, spec));
}
Expand All @@ -75,7 +78,7 @@ public static IndeterminateDrawable<CircularProgressIndicatorSpec> createCircula
@NonNull Context context, @NonNull CircularProgressIndicatorSpec spec) {
return new IndeterminateDrawable<>(
context,
/*baseSpec=*/ spec,
/* baseSpec= */ spec,
new CircularDrawingDelegate(spec),
new CircularIndeterminateAnimatorDelegate(spec));
}
Expand Down Expand Up @@ -140,22 +143,68 @@ public void draw(@NonNull Canvas canvas) {
canvas.save();
drawingDelegate.validateSpecAndAdjustCanvas(canvas, getBounds(), getGrowFraction());

// Draws the track.
drawingDelegate.fillTrack(canvas, paint);
// Draws the indicators.
if (baseSpec.indicatorTrackGapSize > 0) {
if (drawingDelegate instanceof LinearDrawingDelegate) {
((LinearProgressIndicatorSpec) drawingDelegate.spec).trackStopIndicatorSize = 0;
} else if (drawingDelegate instanceof CircularDrawingDelegate) {
// TODO: workaround preventing exiting the indicatorTrackGapSize > 0 logic while keeping
// the animation smooth.
baseSpec.indicatorTrackGapSize = 1;
}

// Draws the transparent track.
int trackColor = baseSpec.trackColor;
baseSpec.trackColor = Color.TRANSPARENT;
drawingDelegate.fillTrack(canvas, paint);
baseSpec.trackColor = trackColor;
} else {
drawingDelegate.fillTrack(canvas, paint);
}

for (int segmentIndex = 0;
segmentIndex < animatorDelegate.segmentColors.length;
segmentIndex++) {

// Draws the actual indicators.
drawingDelegate.fillIndicator(
canvas,
paint,
animatorDelegate.segmentPositions[2 * segmentIndex],
animatorDelegate.segmentPositions[2 * segmentIndex + 1],
animatorDelegate.segmentColors[segmentIndex]);

if (drawingDelegate instanceof LinearDrawingDelegate && baseSpec.indicatorTrackGapSize > 0) {
// Draws the track using fake indicators around the current indicator.
drawTrackIndicators(canvas, segmentIndex);
}
}

canvas.restore();
}

private void drawTrackIndicators(@NonNull Canvas canvas, int segmentIndex) {
int trackColorWithAlpha =
MaterialColors.compositeARGBWithAlpha(baseSpec.trackColor, getAlpha());
float previousSegmentEndPosition =
segmentIndex == 0 ? 0f : animatorDelegate.segmentPositions[2 * segmentIndex - 1];
// Draws the fake indicators as the track to the left of the current indicator.
drawingDelegate.fillIndicator(
canvas,
paint,
previousSegmentEndPosition,
animatorDelegate.segmentPositions[2 * segmentIndex],
trackColorWithAlpha);
if (segmentIndex == animatorDelegate.segmentColors.length - 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,
trackColorWithAlpha);
}
}

// ******************* Setter and getter *******************

@NonNull
Expand Down

0 comments on commit bc59873

Please sign in to comment.