1818
1919import com .google .android .material .R ;
2020
21+ import static java .lang .Math .min ;
22+
2123import android .animation .TimeInterpolator ;
2224import android .animation .ValueAnimator ;
2325import android .content .Context ;
@@ -57,7 +59,6 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
5759 private DrawingDelegate <S > drawingDelegate ;
5860
5961 // Animation.
60- private final SpringForce springForce ;
6162 private final SpringAnimation springAnimation ;
6263 // Active indicator for the progress.
6364 private final ActiveIndicator activeIndicator ;
@@ -83,13 +84,11 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
8384 activeIndicator .isDeterminate = true ;
8485
8586 // Initializes a spring animator for progress animation.
86- springForce = new SpringForce ();
87-
88- springForce .setDampingRatio (SpringForce .DAMPING_RATIO_NO_BOUNCY );
89- springForce .setStiffness (SPRING_FORCE_STIFFNESS );
90-
9187 springAnimation = new SpringAnimation (this , INDICATOR_LENGTH_IN_LEVEL );
92- springAnimation .setSpring (springForce );
88+ springAnimation .setSpring (
89+ new SpringForce ()
90+ .setDampingRatio (SpringForce .DAMPING_RATIO_NO_BOUNCY )
91+ .setStiffness (SPRING_FORCE_STIFFNESS ));
9392
9493 // Initializes a linear animator to enforce phase animation when progress is unchanged.
9594 phaseAnimator = new ValueAnimator ();
@@ -231,7 +230,9 @@ boolean setVisibleInternal(boolean visible, boolean restart, boolean animate) {
231230 skipAnimationOnLevelChange = true ;
232231 } else {
233232 skipAnimationOnLevelChange = false ;
234- springForce .setStiffness (SPRING_FORCE_STIFFNESS / systemAnimatorDurationScale );
233+ springAnimation
234+ .getSpring ()
235+ .setStiffness (SPRING_FORCE_STIFFNESS / systemAnimatorDurationScale );
235236 }
236237
237238 return changed ;
@@ -260,12 +261,31 @@ protected boolean onLevelChange(int level) {
260261 setIndicatorFraction ((float ) level / MAX_DRAWABLE_LEVEL );
261262 setAmplitudeFraction (nextAmplitudeFraction );
262263 } else {
264+ // Update min visible change to the recommended value.
265+ updateSpringMinVisibleChange ();
263266 springAnimation .setStartValue (getIndicatorFraction () * MAX_DRAWABLE_LEVEL );
264267 springAnimation .animateToFinalPosition (level );
265268 }
266269 return true ;
267270 }
268271
272+ /**
273+ * Updates the minimum visible change of the spring animation controlling the indicator length
274+ * (progress) to the recommended value based on the track's length.
275+ */
276+ private void updateSpringMinVisibleChange () {
277+ int width = getBounds ().width ();
278+ int height = getBounds ().height ();
279+ if (drawingDelegate instanceof LinearDrawingDelegate ) {
280+ // Track length is the width of the drawable.
281+ springAnimation .setMinimumVisibleChange ((float ) MAX_DRAWABLE_LEVEL / width );
282+ } else {
283+ // Track length is the perimeter of the circle fit in the drawable.
284+ springAnimation .setMinimumVisibleChange (
285+ (float ) (MAX_DRAWABLE_LEVEL / (min (height , width ) * Math .PI )));
286+ }
287+ }
288+
269289 @ Override
270290 public int getIntrinsicWidth () {
271291 return drawingDelegate .getPreferredWidth ();
@@ -276,6 +296,17 @@ public int getIntrinsicHeight() {
276296 return drawingDelegate .getPreferredHeight ();
277297 }
278298
299+ /** Returns the spring force of the spring animation for the progress. */
300+ @ NonNull
301+ public SpringForce getSpringForce () {
302+ return springAnimation .getSpring ();
303+ }
304+
305+ /** Sets the spring force of the spring animation for the progress. */
306+ public void setSpringForce (@ NonNull SpringForce spring ) {
307+ springAnimation .setSpring (spring );
308+ }
309+
279310 /**
280311 * Sets the drawable level with a fraction [0,1] of the progress. Note: this function is not used
281312 * to force updating the level in opposite to the automatic level updates by framework {@link
0 commit comments