16
16
17
17
package com .google .android .material .progressindicator ;
18
18
19
+ import com .google .android .material .R ;
20
+
21
+ import android .animation .TimeInterpolator ;
19
22
import android .animation .ValueAnimator ;
20
23
import android .content .Context ;
21
24
import android .graphics .Canvas ;
28
31
import androidx .dynamicanimation .animation .FloatPropertyCompat ;
29
32
import androidx .dynamicanimation .animation .SpringAnimation ;
30
33
import androidx .dynamicanimation .animation .SpringForce ;
34
+ import com .google .android .material .animation .AnimationUtils ;
35
+ import com .google .android .material .motion .MotionUtils ;
31
36
import com .google .android .material .progressindicator .DrawingDelegate .ActiveIndicator ;
32
37
33
38
/** This class draws the graphics for determinate mode. */
34
39
public final class DeterminateDrawable <S extends BaseProgressIndicatorSpec >
35
40
extends DrawableWithAnimatedVisibilityChange {
36
41
// Constants for drawing progress.
37
42
private static final int MAX_DRAWABLE_LEVEL = 10000 ;
43
+ // Constants for amplitude animation.
44
+ private static final float FULL_AMPLITUDE_FRACTION_MIN = 0.1f ;
45
+ private static final float FULL_AMPLITUDE_FRACTION_MAX = 0.9f ;
46
+
38
47
// The constant for spring force stiffness.
39
48
private static final float SPRING_FORCE_STIFFNESS = SpringForce .STIFFNESS_VERY_LOW ;
40
49
// If the progress is less than 1%, the gap will be proportional to the progress. So that, it
41
50
// draws a full track at 0%.
42
51
static final float GAP_RAMP_DOWN_THRESHOLD = 0.01f ;
43
52
// The duration of repeated initial phase animation in ms. It can be any positive values.
44
53
private static final int PHASE_ANIMATION_DURATION_MS = 1000 ;
54
+ // The duration of amplitude ramping animation in ms.
55
+ private static final int AMPLITUDE_ANIMATION_DURATION_MS = 500 ;
45
56
46
57
// Drawing delegate object.
47
58
private DrawingDelegate <S > drawingDelegate ;
@@ -51,10 +62,16 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
51
62
private final SpringAnimation springAnimation ;
52
63
// Active indicator for the progress.
53
64
private final ActiveIndicator activeIndicator ;
65
+ // Fraction of displayed amplitude.
66
+ private float targetAmplitudeFraction ;
54
67
// Whether to skip the spring animation on level change event.
55
68
private boolean skipAnimationOnLevelChange = false ;
56
69
57
70
@ NonNull private final ValueAnimator phaseAnimator ;
71
+ @ NonNull private final ValueAnimator amplitudeAnimator ;
72
+ private TimeInterpolator amplitudeInterpolator ;
73
+ @ NonNull private final TimeInterpolator amplitudeOnInterpolator ;
74
+ @ NonNull private final TimeInterpolator amplitudeOffInterpolator ;
58
75
59
76
DeterminateDrawable (
60
77
@ NonNull Context context ,
@@ -87,6 +104,25 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
87
104
});
88
105
phaseAnimator .start ();
89
106
107
+ // Initializes a linear animator to turn on/off wave amplitude.
108
+ amplitudeOnInterpolator =
109
+ MotionUtils .resolveThemeInterpolator (
110
+ context , R .attr .motionEasingStandardInterpolator , AnimationUtils .LINEAR_INTERPOLATOR );
111
+ amplitudeOffInterpolator =
112
+ MotionUtils .resolveThemeInterpolator (
113
+ context ,
114
+ R .attr .motionEasingEmphasizedAccelerateInterpolator ,
115
+ AnimationUtils .LINEAR_INTERPOLATOR );
116
+ amplitudeAnimator = new ValueAnimator ();
117
+ amplitudeAnimator .setDuration (AMPLITUDE_ANIMATION_DURATION_MS );
118
+ amplitudeAnimator .setFloatValues (0 , 1 );
119
+ amplitudeAnimator .setInterpolator (null );
120
+ amplitudeAnimator .addUpdateListener (
121
+ animation -> {
122
+ activeIndicator .amplitudeFraction =
123
+ amplitudeInterpolator .getInterpolation (amplitudeAnimator .getAnimatedFraction ());
124
+ });
125
+
90
126
setGrowFraction (1f );
91
127
}
92
128
@@ -209,9 +245,11 @@ public void jumpToCurrentState() {
209
245
*/
210
246
@ Override
211
247
protected boolean onLevelChange (int level ) {
248
+ float nextAmplitudeFraction = getAmplitudeFractionFromLevel (level );
212
249
if (skipAnimationOnLevelChange ) {
213
250
springAnimation .skipToEnd ();
214
251
setIndicatorFraction ((float ) level / MAX_DRAWABLE_LEVEL );
252
+ setAmplitudeFraction (nextAmplitudeFraction );
215
253
} else {
216
254
springAnimation .setStartValue (getIndicatorFraction () * MAX_DRAWABLE_LEVEL );
217
255
springAnimation .animateToFinalPosition (level );
@@ -240,6 +278,30 @@ void setLevelByFraction(float fraction) {
240
278
setLevel ((int ) (MAX_DRAWABLE_LEVEL * fraction ));
241
279
}
242
280
281
+ private float getAmplitudeFractionFromLevel (int level ) {
282
+ return level >= FULL_AMPLITUDE_FRACTION_MIN * MAX_DRAWABLE_LEVEL
283
+ && level <= FULL_AMPLITUDE_FRACTION_MAX * MAX_DRAWABLE_LEVEL
284
+ ? 1f
285
+ : 0f ;
286
+ }
287
+
288
+ private void maybeStartAmplitudeAnimator (int level ) {
289
+ float newAmplitudeFraction = getAmplitudeFractionFromLevel (level );
290
+ if (newAmplitudeFraction != targetAmplitudeFraction ) {
291
+ if (amplitudeAnimator .isRunning ()) {
292
+ amplitudeAnimator .cancel ();
293
+ }
294
+ targetAmplitudeFraction = newAmplitudeFraction ;
295
+ if (targetAmplitudeFraction == 1f ) {
296
+ amplitudeInterpolator = amplitudeOnInterpolator ;
297
+ amplitudeAnimator .start ();
298
+ } else {
299
+ amplitudeInterpolator = amplitudeOffInterpolator ;
300
+ amplitudeAnimator .reverse ();
301
+ }
302
+ }
303
+ }
304
+
243
305
// ******************* Drawing methods *******************
244
306
245
307
@ Override
@@ -306,6 +368,11 @@ private void setIndicatorFraction(float indicatorFraction) {
306
368
invalidateSelf ();
307
369
}
308
370
371
+ private void setAmplitudeFraction (float amplitudeFraction ) {
372
+ this .activeIndicator .amplitudeFraction = amplitudeFraction ;
373
+ invalidateSelf ();
374
+ }
375
+
309
376
@ NonNull
310
377
DrawingDelegate <S > getDrawingDelegate () {
311
378
return drawingDelegate ;
@@ -327,6 +394,7 @@ public float getValue(DeterminateDrawable<?> drawable) {
327
394
@ Override
328
395
public void setValue (DeterminateDrawable <?> drawable , float value ) {
329
396
drawable .setIndicatorFraction (value / MAX_DRAWABLE_LEVEL );
397
+ drawable .maybeStartAmplitudeAnimator ((int ) value );
330
398
}
331
399
};
332
400
}
0 commit comments