From 2e97dd672eafe77c565895daa67d3580a9cf13aa Mon Sep 17 00:00:00 2001
From: pekingme <8545955+pekingme@users.noreply.github.com>
Date: Wed, 5 Mar 2025 05:39:05 +0000
Subject: [PATCH] [ProgressIndicator] Added fraction support in track corner
radius.
PiperOrigin-RevId: 733576933
---
.../ProgressIndicatorMainDemoFragment.java | 10 +-
docs/components/ProgressIndicator.md | 162 +++++++++---------
.../BaseProgressIndicator.java | 31 +++-
.../BaseProgressIndicatorSpec.java | 53 +++++-
.../CircularDrawingDelegate.java | 9 +-
.../LinearDrawingDelegate.java | 10 +-
.../LinearProgressIndicatorSpec.java | 10 +-
.../progressindicator/res/values/attrs.xml | 4 +-
.../progressindicator/res/values/styles.xml | 8 +-
9 files changed, 183 insertions(+), 114 deletions(-)
diff --git a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
index 9fbf37189..f0e13d183 100644
--- a/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
+++ b/catalog/java/io/material/catalog/progressindicator/ProgressIndicatorMainDemoFragment.java
@@ -80,7 +80,7 @@ public class ProgressIndicatorMainDemoFragment extends ProgressIndicatorDemoFrag
Slider thicknessSlider = view.findViewById(R.id.thicknessSlider);
thicknessSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newThickness = Math.round(value * pixelsInDp);
+ int newThickness = (int) (value * pixelsInDp);
if (linearIndicator.getTrackThickness() != newThickness) {
linearIndicator.setTrackThickness(newThickness);
}
@@ -92,7 +92,7 @@ public class ProgressIndicatorMainDemoFragment extends ProgressIndicatorDemoFrag
Slider cornerSlider = view.findViewById(R.id.cornerSlider);
cornerSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newCornerRadius = Math.round(value * pixelsInDp);
+ int newCornerRadius = (int) (value * pixelsInDp);
if (linearIndicator.getTrackCornerRadius() != newCornerRadius) {
linearIndicator.setTrackCornerRadius(newCornerRadius);
}
@@ -104,7 +104,7 @@ public class ProgressIndicatorMainDemoFragment extends ProgressIndicatorDemoFrag
Slider gapSlider = view.findViewById(R.id.gapSlider);
gapSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newGapSize = Math.round(value * pixelsInDp);
+ int newGapSize = (int) (value * pixelsInDp);
if (linearIndicator.getIndicatorTrackGapSize() != newGapSize) {
linearIndicator.setIndicatorTrackGapSize(newGapSize);
}
@@ -125,7 +125,7 @@ public class ProgressIndicatorMainDemoFragment extends ProgressIndicatorDemoFrag
Slider linearStopIndicatorSlider = view.findViewById(R.id.linearStopIndicatorSizeSlider);
linearStopIndicatorSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newStopIndicatorSize = Math.round(value * pixelsInDp);
+ int newStopIndicatorSize = (int) (value * pixelsInDp);
if (linearIndicator.getTrackStopIndicatorSize() != newStopIndicatorSize) {
linearIndicator.setTrackStopIndicatorSize(newStopIndicatorSize);
}
@@ -134,7 +134,7 @@ public class ProgressIndicatorMainDemoFragment extends ProgressIndicatorDemoFrag
Slider circularSizeSlider = view.findViewById(R.id.circularSizeSlider);
circularSizeSlider.addOnChangeListener(
(slider, value, fromUser) -> {
- int newCornerRadius = Math.round(value * pixelsInDp);
+ int newCornerRadius = (int) (value * pixelsInDp);
if (circularIndicator.getIndicatorSize() != newCornerRadius) {
circularIndicator.setIndicatorSize(newCornerRadius);
}
diff --git a/docs/components/ProgressIndicator.md b/docs/components/ProgressIndicator.md
index 4760fe123..998e3ff5f 100644
--- a/docs/components/ProgressIndicator.md
+++ b/docs/components/ProgressIndicator.md
@@ -50,12 +50,10 @@ A determinate progress indicator can be added to a layout:
```xml
@@ -187,13 +185,13 @@ In the layout:
```xml
@@ -201,9 +199,8 @@ In the layout:
```xml
+ same color at a time.
+
* `contiguous` - animates as repeated cycles with three adjacent segments in
- different colors.
+ different colors.
+
**Note:** There is a minimum requirement of 3 indicator colors to use the
**contiguous** animation. Otherwise, an IllegalArgumentException will be thrown.
@@ -245,7 +240,8 @@ API and source code:
The following example shows a determinate circular progress indicator.
-
@@ -253,13 +249,13 @@ In the layout:
```xml
@@ -267,9 +263,8 @@ In the layout:
```xml
`getTrackThickness` | `4dp`
-**Indicator color** | `app:indicatorColor` | `setIndicatorColor`
`getIndicatorColor` | `colorPrimary`
-**Track color** | `app:trackColor` | `setTrackColor`
`getTrackColor` | `colorPrimaryContainer` (linear)
`@android:color/transparent` (circular)
-**Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius`
`getTrackCornerRadius` | `2dp`
-**Indicator track gap size** | `app:indicatorTrackGapSize` | `setIndicatorTrackGapSize`
`getIndicatorTrackGapSize` | `4dp`
-**Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior`
`getShowAnimationBehavior` | `none`
-**Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior`
`getHideAnimationBehavior` | `none`
-**Delay (in ms) to show** | `app:showDelay` | N/A | 0
-**Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0
-**Wavelength** | `app:wavelength` | `setWavelength` | 0
-**Wavelength in determinate mode** | `app:wavelengthDeterminate` | `setWavelengthDeterminate`
`getWavelenthDeterminate` | `wavelength`
-**Wavelength in indeterminate mode** | `app:wavelengthIndeterminate` | `setWavelengthIndeterminate`
`getWavelengthIndeterminate` | `wavelength`
-**Wave amplitude** | `app:waveAmplitude` | `setWaveAmplitude`
`getWaveAmplitude` | 0
-**Wave speed** | `app:waveSpeed` | `setWaveSpeed`
`getWaveSpeed` | 0
-**Indeterminate animator duration scale** | `app:indeterminateAnimatorDurationScale` | `setIndeterminateAnimatorDurationScale` | 1
+Element | Attribute | Related method(s) | Default value
+----------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------- | -------------
+**Track thickness** | `app:trackThickness` | `setTrackThickness`
`getTrackThickness` | `4dp`
+**Indicator color** | `app:indicatorColor` | `setIndicatorColor`
`getIndicatorColor` | `colorPrimary`
+**Track color** | `app:trackColor` | `setTrackColor`
`getTrackColor` | `colorPrimaryContainer` (linear)
`@android:color/transparent` (circular)
+**Track corner radius** | `app:trackCornerRadius` | `setTrackCornerRadius`
`setTrackCornerRadiusFraction`
`getTrackCornerRadius` | `50%`
+**Indicator track gap size** | `app:indicatorTrackGapSize` | `setIndicatorTrackGapSize`
`getIndicatorTrackGapSize` | `4dp`
+**Show animation behavior** | `app:showAnimationBehavior` | `setShowAnimationBehavior`
`getShowAnimationBehavior` | `none`
+**Hide animation behavior** | `app:hideAnimationBehavior` | `setHideAnimationBehavior`
`getHideAnimationBehavior` | `none`
+**Delay (in ms) to show** | `app:showDelay` | N/A | 0
+**Min delay (in ms) to hide** | `app:minHideDelay` | N/A | 0
+**Wavelength** | `app:wavelength` | `setWavelength` | 0
+**Wavelength in determinate mode** | `app:wavelengthDeterminate` | `setWavelengthDeterminate`
`getWavelenthDeterminate` | `wavelength`
+**Wavelength in indeterminate mode** | `app:wavelengthIndeterminate` | `setWavelengthIndeterminate`
`getWavelengthIndeterminate` | `wavelength`
+**Wave amplitude** | `app:waveAmplitude` | `setWaveAmplitude`
`getWaveAmplitude` | 0
+**Wave speed** | `app:waveSpeed` | `setWaveSpeed`
`getWaveSpeed` | 0
+**Indeterminate animator duration scale** | `app:indeterminateAnimatorDurationScale` | `setIndeterminateAnimatorDurationScale` | 1
#### Linear type specific attributes
@@ -314,7 +309,7 @@ Element | Attribute | Related me
**Indeterminate animation type** | `app:indeterminateAnimationType` | `setIndeterminateAnimationType`
`getIndeterminateAnimationType` | `disjoint`
**Indicator direction** | `app:indicatorDirectionLinear` | `setIndicatorDirection`
`getIndicatorDirection` | `leftToRight`
**Track stop indicator size** | `app:trackStopIndicatorSize` | `setTrackStopIndicatorSize`
`getTrackStopIndicatorSize` | `4dp`
-**Track inner corner radius** | `app:trackInnerCornerRadius` | `setTrackInnerCornerRadius`
`setTrackInnerCornerRadiusFraction`
`getTrackInnerCornerRadius` | `none`
+**Track inner corner radius** | `app:trackInnerCornerRadius` | `setTrackInnerCornerRadius`
`setTrackInnerCornerRadiusFraction`
`getTrackInnerCornerRadius` | `none` (use `trackCornerRadius`)
#### Circular type specific attributes
@@ -352,16 +347,16 @@ In order to comply with the latest accessibility requirements, the
`LinearProgressIndicator` and `CircularProgressIndicator` have been updated with
additional attributes:
-- `app:indicatorTrackGapSize`: size of the gap between the indicator and the
- track, 4dp by default.
-- `app:trackStopIndicatorSize`: size of the stop at the end of the track, 4dp by
- default. Only applies to the linear determinate configuration.
+- `app:indicatorTrackGapSize`: size of the gap between the indicator and the
+ track, 4dp by default.
+- `app:trackStopIndicatorSize`: size of the stop at the end of the track, 4dp
+ by default. Only applies to the linear determinate configuration.
`*.Legacy` styles have been added to revert to the previous behavior (**not
recommended**):
-- `Widget.Material3.LinearProgressIndicator.Legacy`
-- `Widget.Material3.CircularProgressIndicator.Legacy`
+- `Widget.Material3.LinearProgressIndicator.Legacy`
+- `Widget.Material3.CircularProgressIndicator.Legacy`
## Theming
@@ -391,8 +386,8 @@ circular progress indicators and affects other components:
```xml
```
@@ -401,17 +396,22 @@ all circular progress indicators but does not affect other components:
```xml
-
```
@@ -419,7 +419,5 @@ Use the style in the layout, which affects only this specific circular progress
indicator:
```xml
-
*/
public void setTrackCornerRadius(@Px int trackCornerRadius) {
if (spec.trackCornerRadius != trackCornerRadius) {
- spec.trackCornerRadius = Math.round(min(trackCornerRadius, spec.trackThickness / 2f));
+ spec.trackCornerRadius = min(trackCornerRadius, spec.trackThickness / 2);
+ spec.useRelativeTrackCornerRadius = false;
+ invalidate();
+ }
+ }
+
+ /**
+ * Returns the relative radius of the rounded corner for the indicator and track in pixels.
+ *
+ * @see #setTrackCornerRadiusFraction(float)
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_trackCornerRadius
+ */
+ public float getTrackCornerRadiusFraction() {
+ return spec.trackCornerRadiusFraction;
+ }
+
+ /**
+ * Sets the radius of the rounded corner for the indicator and track in fraction of track
+ * thickness.
+ *
+ * @param fraction The fraction of corner radius to track thickness.
+ * @see #getTrackCornerRadiusFraction()
+ * @attr ref
+ * com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_trackCornerRadius
+ */
+ public void setTrackCornerRadiusFraction(@FloatRange(from = 0f, to = 0.5f) float fraction) {
+ if (spec.trackCornerRadiusFraction != fraction) {
+ spec.trackCornerRadiusFraction = min(fraction, 0.5f);
+ spec.useRelativeTrackCornerRadius = true;
invalidate();
}
}
diff --git a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
index 7c17a8528..5f99166e5 100644
--- a/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
+++ b/lib/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java
@@ -54,6 +54,18 @@ public abstract class BaseProgressIndicatorSpec {
*/
@Px public int trackCornerRadius;
+ /**
+ * The fraction of the track thickness to be used as the corner radius. And the stroke ROUND cap
+ * is used to prevent artifacts like (b/319309456), when 0.5f is specified.
+ */
+ public float trackCornerRadiusFraction;
+
+ /**
+ * When this is true, the {#link trackCornerRadiusFraction} takes effect. Otherwise, the {@link
+ * trackCornerRadius} takes effect.
+ */
+ public boolean useRelativeTrackCornerRadius;
+
/**
* The color array used in the indicator. In determinate mode, only the first item will be used.
*/
@@ -112,11 +124,21 @@ public abstract class BaseProgressIndicatorSpec {
trackThickness =
getDimensionPixelSize(
context, a, R.styleable.BaseProgressIndicator_trackThickness, defaultIndicatorSize);
- trackCornerRadius =
- min(
- getDimensionPixelSize(
- context, a, R.styleable.BaseProgressIndicator_trackCornerRadius, 0),
- Math.round(trackThickness / 2f));
+ TypedValue trackCornerRadiusValue =
+ a.peekValue(R.styleable.BaseProgressIndicator_trackCornerRadius);
+ if (trackCornerRadiusValue != null) {
+ if (trackCornerRadiusValue.type == TypedValue.TYPE_DIMENSION) {
+ trackCornerRadius =
+ min(
+ TypedValue.complexToDimensionPixelSize(
+ trackCornerRadiusValue.data, a.getResources().getDisplayMetrics()),
+ trackThickness / 2);
+ useRelativeTrackCornerRadius = false;
+ } else if (trackCornerRadiusValue.type == TypedValue.TYPE_FRACTION) {
+ trackCornerRadiusFraction = min(trackCornerRadiusValue.getFraction(1.0f, 1.0f), 0.5f);
+ useRelativeTrackCornerRadius = true;
+ }
+ }
showAnimationBehavior =
a.getInt(
R.styleable.BaseProgressIndicator_showAnimationBehavior,
@@ -222,6 +244,27 @@ public abstract class BaseProgressIndicatorSpec {
&& ((!isDeterminate && wavelengthIndeterminate > 0)
|| (isDeterminate && wavelengthDeterminate > 0));
}
+
+ /**
+ * Returns the track corner radius in pixels.
+ *
+ *
If {@link #useRelativeTrackCornerRadius} is true, the track corner radius is calculated
+ * using the track thickness and the track corner radius fraction. Otherwise, the track corner
+ * radius is returned directly.
+ */
+ public int getTrackCornerRadiusInPx() {
+ return useRelativeTrackCornerRadius
+ ? (int) (trackThickness * trackCornerRadiusFraction)
+ : trackCornerRadius;
+ }
+
+ /**
+ * Returns true if the stroke ROUND cap should be used to prevent artifacts like (b/319309456),
+ * when fully rounded corners are specified.
+ */
+ public boolean useStrokeCap() {
+ return useRelativeTrackCornerRadius && trackCornerRadiusFraction == 0.5f;
+ }
@CallSuper
void validateSpec() {
diff --git a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
index 737fe4e3b..6a2e16d89 100644
--- a/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
+++ b/lib/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java
@@ -58,8 +58,6 @@ final class CircularDrawingDelegate extends DrawingDelegate