Skip to content

Commit a5890bf

Browse files
pekingmewcshi
authored andcommitted
[ProgressIndicator] Fixed incorrect visibility when Recyclerview is used.
Resolves #1662 PiperOrigin-RevId: 341845553
1 parent f664c11 commit a5890bf

File tree

7 files changed

+80
-42
lines changed

7 files changed

+80
-42
lines changed

lib/java/com/google/android/material/progressindicator/BaseProgressIndicator.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,35 +237,38 @@ public void hide() {
237237
* @see #hide()
238238
*/
239239
private void internalHide() {
240-
getCurrentDrawable().setVisible(/*visible=*/ false, /*restart=*/ false);
240+
getCurrentDrawable()
241+
.setVisible(/*visible=*/ false, /*restart=*/ false, /*animationDesired=*/ true);
241242

242243
if (isNoLongerNeedToBeVisible()) {
243244
setVisibility(INVISIBLE);
244245
}
245246
}
246247

247248
@Override
248-
protected void onVisibilityChanged(@NonNull View changeView, int visibility) {
249-
super.onVisibilityChanged(changeView, visibility);
250-
applyNewVisibility();
249+
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
250+
super.onVisibilityChanged(changedView, visibility);
251+
applyNewVisibility(/*animationDesired=*/ visibility == VISIBLE);
251252
}
252253

253254
@Override
254255
protected void onWindowVisibilityChanged(int visibility) {
255256
super.onWindowVisibilityChanged(visibility);
256-
applyNewVisibility();
257+
applyNewVisibility(/*animationDesired=*/ false);
257258
}
258259

259260
/**
260261
* If it changes to visible, the start animation will be started if {@code growMode} indicates
261262
* any. If it changes to invisible, hides the drawable immediately.
263+
*
264+
* @param animationDesired Whether to change the visibility with animation.
262265
*/
263-
protected void applyNewVisibility() {
266+
protected void applyNewVisibility(boolean animationDesired) {
264267
if (!isParentDoneInitializing) {
265268
return;
266269
}
267270

268-
getCurrentDrawable().setVisible(visibleToUser(), /*restart=*/ false);
271+
getCurrentDrawable().setVisible(visibleToUser(), /*restart=*/ false, animationDesired);
269272
}
270273

271274
@Override

lib/java/com/google/android/material/progressindicator/CircularProgressIndicator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ private void initializeDrawables() {
102102
setProgressDrawable(
103103
new DeterminateDrawable(
104104
getContext(), baseSpec, behavior, new CircularDrawingDelegate(spec)));
105-
applyNewVisibility();
106105
}
107106

108107
// **************** Getters and setters ****************

lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ void setLevelByFraction(float fraction) {
9898
* false}, otherwise.
9999
*/
100100
@Override
101-
public boolean setVisible(boolean visible, boolean restart, boolean animationDesired) {
102-
boolean changed = super.setVisible(visible, restart, animationDesired);
101+
protected boolean setVisibleInternal(boolean visible, boolean restart, boolean animationDesired) {
102+
boolean changed = super.setVisibleInternal(visible, restart, animationDesired);
103103

104104
float systemAnimatorDurationScale =
105105
animatorDurationScaleProvider.getSystemAnimatorDurationScale(context.getContentResolver());

lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ abstract class DrawableWithAnimatedVisibilityChange extends Drawable implements
6868
private List<AnimationCallback> animationCallbacks;
6969
// An internal AnimationCallback which is executed before the user's animation callbacks.
7070
private AnimationCallback internalAnimationCallback;
71+
// Flag to ignore all external callbacks.
72+
private boolean ignoreCallbacks;
7173

7274
// A fraction from 0 to 1 indicating the ratio used in drawing, controlled by show/hide animator.
7375
private float growFraction;
@@ -165,7 +167,7 @@ private void dispatchAnimationStart() {
165167
if (internalAnimationCallback != null) {
166168
internalAnimationCallback.onAnimationStart(this);
167169
}
168-
if (animationCallbacks != null) {
170+
if (animationCallbacks != null && !ignoreCallbacks) {
169171
for (AnimationCallback callback : animationCallbacks) {
170172
callback.onAnimationStart(this);
171173
}
@@ -177,7 +179,7 @@ private void dispatchAnimationEnd() {
177179
if (internalAnimationCallback != null) {
178180
internalAnimationCallback.onAnimationEnd(this);
179181
}
180-
if (animationCallbacks != null) {
182+
if (animationCallbacks != null && !ignoreCallbacks) {
181183
for (AnimationCallback callback : animationCallbacks) {
182184
callback.onAnimationEnd(this);
183185
}
@@ -211,48 +213,66 @@ public boolean isHiding() {
211213
return (hideAnimator != null && hideAnimator.isRunning()) || mockHideAnimationRunning;
212214
}
213215

214-
/** Hides the drawable immediately */
215-
public boolean hideNow(){
216+
/** Hides the drawable immediately without triggering animation callbacks. */
217+
public boolean hideNow() {
216218
return setVisible(/*visible=*/ false, /*restart=*/ false, /*animationDesired=*/ false);
217219
}
218220

221+
@Override
222+
public boolean setVisible(boolean visible, boolean restart) {
223+
return setVisible(visible, restart, /*animationDesired=*/ true);
224+
}
225+
219226
/**
220-
* Sets the visibility with/without animation based on system animator duration scale.
227+
* Changes the visibility with/without triggering the animation callbacks.
221228
*
229+
* @param visible Whether to make the drawable visible.
230+
* @param restart Whether to force starting the animation from the beginning.
231+
* @param animationDesired Whether to change the visibility with animation.
232+
* @return {@code true}, if the visibility changes or will change after the animation; {@code
233+
* false}, otherwise.
222234
* @see #setVisible(boolean, boolean, boolean)
223235
*/
224-
@Override
225-
public boolean setVisible(boolean visible, boolean restart) {
236+
public boolean setVisible(boolean visible, boolean restart, boolean animationDesired) {
226237
float systemAnimatorDurationScale =
227238
animatorDurationScaleProvider.getSystemAnimatorDurationScale(context.getContentResolver());
228239
// Only show/hide the drawable with animations if system animator duration scale is not off and
229240
// some grow mode is used.
230-
return setVisible(visible, restart, systemAnimatorDurationScale > 0);
241+
return setVisibleInternal(
242+
visible, restart, animationDesired && systemAnimatorDurationScale > 0);
231243
}
232244

233245
/**
234-
* Show or hide the drawable with/without animation effects.
246+
* Show or hide the drawable with/without animation effects and/or animation callbacks.
235247
*
236248
* @param visible Whether to make the drawable visible.
237249
* @param restart Whether to force starting the animation from the beginning.
238250
* @param animationDesired Whether to change the visibility with animation.
239251
* @return {@code true}, if the visibility changes or will change after the animation; {@code
240252
* false}, otherwise.
241253
*/
242-
public boolean setVisible(boolean visible, boolean restart, boolean animationDesired) {
254+
protected boolean setVisibleInternal(boolean visible, boolean restart, boolean animationDesired) {
255+
maybeInitializeAnimators();
243256
if (!isVisible() && !visible) {
244-
// Early return if trying to hide a hidden drawable.
257+
// Early returns if trying to hide a hidden drawable.
245258
return false;
246259
}
247260

248-
maybeInitializeAnimators();
261+
ValueAnimator animatorInAction = visible ? showAnimator : hideAnimator;
249262

250-
if (animationDesired && (visible ? showAnimator : hideAnimator).isRunning()) {
251-
// Show/hide animation should not be reset while being played.
252-
return false;
263+
if (!animationDesired) {
264+
if (animatorInAction.isRunning()) {
265+
// Show/hide animation should fast-forward to the end without callbacks.
266+
endAnimatorWithoutCallbacks(animatorInAction);
267+
}
268+
// Immediately updates the drawable's visibility without animation if not desired.
269+
return super.setVisible(visible, DEFAULT_DRAWABLE_RESTART);
253270
}
254271

255-
ValueAnimator animationInAction = visible ? showAnimator : hideAnimator;
272+
if (animationDesired && animatorInAction.isRunning()) {
273+
// Show/hide animation should not be replayed while playing.
274+
return false;
275+
}
256276

257277
// If requests to show, sets the drawable visible. If requests to hide, the visibility is
258278
// controlled by the animation listener attached to hide animation.
@@ -262,19 +282,28 @@ public boolean setVisible(boolean visible, boolean restart, boolean animationDes
262282
if (!animationDesired) {
263283
// This triggers onAnimationStart() callbacks for showing and onAnimationEnd() callbacks for
264284
// hiding. It also fast-forwards the animator properties to the end state.
265-
animationInAction.end();
285+
animatorInAction.end();
266286
return changed;
267287
}
268288

269-
if (restart || VERSION.SDK_INT < 19 || !animationInAction.isPaused()) {
289+
if (restart || VERSION.SDK_INT < 19 || !animatorInAction.isPaused()) {
270290
// Starts/restarts the animator if requested or not eligible to resume.
271-
animationInAction.start();
291+
animatorInAction.start();
272292
} else {
273-
animationInAction.resume();
293+
animatorInAction.resume();
274294
}
275295
return changed;
276296
}
277297

298+
private void endAnimatorWithoutCallbacks(@NonNull ValueAnimator... animators) {
299+
boolean ignoreCallbacksOrig = ignoreCallbacks;
300+
ignoreCallbacks = true;
301+
for (ValueAnimator animator : animators) {
302+
animator.end();
303+
}
304+
ignoreCallbacks = ignoreCallbacksOrig;
305+
}
306+
278307
// ******************* Getters and setters *******************
279308

280309
@Override

lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import android.graphics.Canvas;
2121
import android.graphics.Rect;
2222
import android.graphics.drawable.Drawable;
23+
import android.os.Build.VERSION;
24+
import android.os.Build.VERSION_CODES;
2325
import androidx.annotation.NonNull;
2426

2527
/**
@@ -59,16 +61,20 @@ public IndeterminateDrawable(
5961
* false}, otherwise.
6062
*/
6163
@Override
62-
public boolean setVisible(boolean visible, boolean restart, boolean animationDesired) {
63-
boolean changed = super.setVisible(visible, restart, animationDesired);
64+
protected boolean setVisibleInternal(boolean visible, boolean restart, boolean animationDesired) {
65+
boolean changed = super.setVisibleInternal(visible, restart, animationDesired);
6466

6567
// Unless it's showing or hiding, cancels and resets main animator.
6668
if (!isRunning()) {
6769
animatorDelegate.cancelAnimatorImmediately();
6870
animatorDelegate.resetPropertiesForNewStart();
6971
}
7072
// Restarts the main animator if it's visible and needs to be animated.
71-
if (visible && animationDesired) {
73+
float systemAnimatorDurationScale =
74+
animatorDurationScaleProvider.getSystemAnimatorDurationScale(context.getContentResolver());
75+
if (visible
76+
&& (animationDesired
77+
|| (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP && systemAnimatorDurationScale > 0))) {
7278
animatorDelegate.startAnimator();
7379
}
7480

lib/java/com/google/android/material/progressindicator/LinearProgressIndicator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ private void initializeDrawables() {
135135
: new LinearIndeterminateSpacingAnimatorDelegate(getContext(), spec)));
136136
setProgressDrawable(
137137
new DeterminateDrawable(getContext(), baseSpec, behavior, new LinearDrawingDelegate(spec)));
138-
applyNewVisibility();
139138
}
140139

141140
// **************** Getters and setters ****************

lib/java/com/google/android/material/progressindicator/ProgressIndicator.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ private void initializeDrawables() {
244244
new DeterminateDrawable(
245245
getContext(), baseSpec, behavior, new LinearDrawingDelegate(linearSpec)));
246246
}
247-
applyNewVisibility();
248247
}
249248

250249
private void registerAnimationCallbacks() {
@@ -334,35 +333,38 @@ public void hide() {
334333
* @see #hide()
335334
*/
336335
private void internalHide() {
337-
getCurrentDrawable().setVisible(/*visible=*/ false, /*restart=*/ false);
336+
getCurrentDrawable()
337+
.setVisible(/*visible=*/ false, /*restart=*/ false, /*animationDesired=*/ true);
338338

339339
if (isNoLongerNeedToBeVisible()) {
340340
setVisibility(INVISIBLE);
341341
}
342342
}
343343

344344
@Override
345-
protected void onVisibilityChanged(View changeView, int visibility) {
346-
super.onVisibilityChanged(changeView, visibility);
347-
applyNewVisibility();
345+
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
346+
super.onVisibilityChanged(changedView, visibility);
347+
applyNewVisibility(/*animationDesired=*/ visibility != GONE);
348348
}
349349

350350
@Override
351351
protected void onWindowVisibilityChanged(int visibility) {
352352
super.onWindowVisibilityChanged(visibility);
353-
applyNewVisibility();
353+
applyNewVisibility(/*animationDesired=*/ false);
354354
}
355355

356356
/**
357357
* If it changes to visible, the start animation will be started if {@code growMode} indicates
358358
* any. If it changes to invisible, hides the drawable immediately.
359+
*
360+
* @param animationDesired Whether to change the visibility with animation.
359361
*/
360-
private void applyNewVisibility() {
362+
private void applyNewVisibility(boolean animationDesired) {
361363
if (!isParentDoneInitializing) {
362364
return;
363365
}
364366

365-
getCurrentDrawable().setVisible(visibleToUser(), /*restart=*/ false);
367+
getCurrentDrawable().setVisible(visibleToUser(), /*restart=*/ false, animationDesired);
366368
}
367369

368370
@Override

0 commit comments

Comments
 (0)