• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.support.v7.widget;
17 
18 import android.support.annotation.NonNull;
19 import android.support.v4.animation.AnimatorCompatHelper;
20 import android.support.v4.view.ViewCompat;
21 import android.support.v4.view.ViewPropertyAnimatorCompat;
22 import android.support.v4.view.ViewPropertyAnimatorListener;
23 import android.support.v7.widget.RecyclerView.ViewHolder;
24 import android.view.View;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * This implementation of {@link RecyclerView.ItemAnimator} provides basic
31  * animations on remove, add, and move events that happen to the items in
32  * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
33  *
34  * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
35  */
36 public class DefaultItemAnimator extends SimpleItemAnimator {
37     private static final boolean DEBUG = false;
38 
39     private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
40     private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
41     private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
42     private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
43 
44     private ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
45     private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
46     private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
47 
48     private ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
49     private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
50     private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
51     private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
52 
53     private static class MoveInfo {
54         public ViewHolder holder;
55         public int fromX, fromY, toX, toY;
56 
MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY)57         private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
58             this.holder = holder;
59             this.fromX = fromX;
60             this.fromY = fromY;
61             this.toX = toX;
62             this.toY = toY;
63         }
64     }
65 
66     private static class ChangeInfo {
67         public ViewHolder oldHolder, newHolder;
68         public int fromX, fromY, toX, toY;
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder)69         private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
70             this.oldHolder = oldHolder;
71             this.newHolder = newHolder;
72         }
73 
ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY)74         private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
75                 int fromX, int fromY, int toX, int toY) {
76             this(oldHolder, newHolder);
77             this.fromX = fromX;
78             this.fromY = fromY;
79             this.toX = toX;
80             this.toY = toY;
81         }
82 
83         @Override
toString()84         public String toString() {
85             return "ChangeInfo{" +
86                     "oldHolder=" + oldHolder +
87                     ", newHolder=" + newHolder +
88                     ", fromX=" + fromX +
89                     ", fromY=" + fromY +
90                     ", toX=" + toX +
91                     ", toY=" + toY +
92                     '}';
93         }
94     }
95 
96     @Override
runPendingAnimations()97     public void runPendingAnimations() {
98         boolean removalsPending = !mPendingRemovals.isEmpty();
99         boolean movesPending = !mPendingMoves.isEmpty();
100         boolean changesPending = !mPendingChanges.isEmpty();
101         boolean additionsPending = !mPendingAdditions.isEmpty();
102         if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
103             // nothing to animate
104             return;
105         }
106         // First, remove stuff
107         for (ViewHolder holder : mPendingRemovals) {
108             animateRemoveImpl(holder);
109         }
110         mPendingRemovals.clear();
111         // Next, move stuff
112         if (movesPending) {
113             final ArrayList<MoveInfo> moves = new ArrayList<>();
114             moves.addAll(mPendingMoves);
115             mMovesList.add(moves);
116             mPendingMoves.clear();
117             Runnable mover = new Runnable() {
118                 @Override
119                 public void run() {
120                     for (MoveInfo moveInfo : moves) {
121                         animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
122                                 moveInfo.toX, moveInfo.toY);
123                     }
124                     moves.clear();
125                     mMovesList.remove(moves);
126                 }
127             };
128             if (removalsPending) {
129                 View view = moves.get(0).holder.itemView;
130                 ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
131             } else {
132                 mover.run();
133             }
134         }
135         // Next, change stuff, to run in parallel with move animations
136         if (changesPending) {
137             final ArrayList<ChangeInfo> changes = new ArrayList<>();
138             changes.addAll(mPendingChanges);
139             mChangesList.add(changes);
140             mPendingChanges.clear();
141             Runnable changer = new Runnable() {
142                 @Override
143                 public void run() {
144                     for (ChangeInfo change : changes) {
145                         animateChangeImpl(change);
146                     }
147                     changes.clear();
148                     mChangesList.remove(changes);
149                 }
150             };
151             if (removalsPending) {
152                 ViewHolder holder = changes.get(0).oldHolder;
153                 ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
154             } else {
155                 changer.run();
156             }
157         }
158         // Next, add stuff
159         if (additionsPending) {
160             final ArrayList<ViewHolder> additions = new ArrayList<>();
161             additions.addAll(mPendingAdditions);
162             mAdditionsList.add(additions);
163             mPendingAdditions.clear();
164             Runnable adder = new Runnable() {
165                 @Override
166                 public void run() {
167                     for (ViewHolder holder : additions) {
168                         animateAddImpl(holder);
169                     }
170                     additions.clear();
171                     mAdditionsList.remove(additions);
172                 }
173             };
174             if (removalsPending || movesPending || changesPending) {
175                 long removeDuration = removalsPending ? getRemoveDuration() : 0;
176                 long moveDuration = movesPending ? getMoveDuration() : 0;
177                 long changeDuration = changesPending ? getChangeDuration() : 0;
178                 long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
179                 View view = additions.get(0).itemView;
180                 ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
181             } else {
182                 adder.run();
183             }
184         }
185     }
186 
187     @Override
animateRemove(final ViewHolder holder)188     public boolean animateRemove(final ViewHolder holder) {
189         resetAnimation(holder);
190         mPendingRemovals.add(holder);
191         return true;
192     }
193 
animateRemoveImpl(final ViewHolder holder)194     private void animateRemoveImpl(final ViewHolder holder) {
195         final View view = holder.itemView;
196         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
197         mRemoveAnimations.add(holder);
198         animation.setDuration(getRemoveDuration())
199                 .alpha(0).setListener(new VpaListenerAdapter() {
200             @Override
201             public void onAnimationStart(View view) {
202                 dispatchRemoveStarting(holder);
203             }
204 
205             @Override
206             public void onAnimationEnd(View view) {
207                 animation.setListener(null);
208                 ViewCompat.setAlpha(view, 1);
209                 dispatchRemoveFinished(holder);
210                 mRemoveAnimations.remove(holder);
211                 dispatchFinishedWhenDone();
212             }
213         }).start();
214     }
215 
216     @Override
animateAdd(final ViewHolder holder)217     public boolean animateAdd(final ViewHolder holder) {
218         resetAnimation(holder);
219         ViewCompat.setAlpha(holder.itemView, 0);
220         mPendingAdditions.add(holder);
221         return true;
222     }
223 
animateAddImpl(final ViewHolder holder)224     private void animateAddImpl(final ViewHolder holder) {
225         final View view = holder.itemView;
226         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
227         mAddAnimations.add(holder);
228         animation.alpha(1).setDuration(getAddDuration()).
229                 setListener(new VpaListenerAdapter() {
230                     @Override
231                     public void onAnimationStart(View view) {
232                         dispatchAddStarting(holder);
233                     }
234                     @Override
235                     public void onAnimationCancel(View view) {
236                         ViewCompat.setAlpha(view, 1);
237                     }
238 
239                     @Override
240                     public void onAnimationEnd(View view) {
241                         animation.setListener(null);
242                         dispatchAddFinished(holder);
243                         mAddAnimations.remove(holder);
244                         dispatchFinishedWhenDone();
245                     }
246                 }).start();
247     }
248 
249     @Override
animateMove(final ViewHolder holder, int fromX, int fromY, int toX, int toY)250     public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
251             int toX, int toY) {
252         final View view = holder.itemView;
253         fromX += ViewCompat.getTranslationX(holder.itemView);
254         fromY += ViewCompat.getTranslationY(holder.itemView);
255         resetAnimation(holder);
256         int deltaX = toX - fromX;
257         int deltaY = toY - fromY;
258         if (deltaX == 0 && deltaY == 0) {
259             dispatchMoveFinished(holder);
260             return false;
261         }
262         if (deltaX != 0) {
263             ViewCompat.setTranslationX(view, -deltaX);
264         }
265         if (deltaY != 0) {
266             ViewCompat.setTranslationY(view, -deltaY);
267         }
268         mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
269         return true;
270     }
271 
animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY)272     private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
273         final View view = holder.itemView;
274         final int deltaX = toX - fromX;
275         final int deltaY = toY - fromY;
276         if (deltaX != 0) {
277             ViewCompat.animate(view).translationX(0);
278         }
279         if (deltaY != 0) {
280             ViewCompat.animate(view).translationY(0);
281         }
282         // TODO: make EndActions end listeners instead, since end actions aren't called when
283         // vpas are canceled (and can't end them. why?)
284         // need listener functionality in VPACompat for this. Ick.
285         final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
286         mMoveAnimations.add(holder);
287         animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
288             @Override
289             public void onAnimationStart(View view) {
290                 dispatchMoveStarting(holder);
291             }
292             @Override
293             public void onAnimationCancel(View view) {
294                 if (deltaX != 0) {
295                     ViewCompat.setTranslationX(view, 0);
296                 }
297                 if (deltaY != 0) {
298                     ViewCompat.setTranslationY(view, 0);
299                 }
300             }
301             @Override
302             public void onAnimationEnd(View view) {
303                 animation.setListener(null);
304                 dispatchMoveFinished(holder);
305                 mMoveAnimations.remove(holder);
306                 dispatchFinishedWhenDone();
307             }
308         }).start();
309     }
310 
311     @Override
animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY)312     public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
313             int fromX, int fromY, int toX, int toY) {
314         if (oldHolder == newHolder) {
315             // Don't know how to run change animations when the same view holder is re-used.
316             // run a move animation to handle position changes.
317             return animateMove(oldHolder, fromX, fromY, toX, toY);
318         }
319         final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
320         final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
321         final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
322         resetAnimation(oldHolder);
323         int deltaX = (int) (toX - fromX - prevTranslationX);
324         int deltaY = (int) (toY - fromY - prevTranslationY);
325         // recover prev translation state after ending animation
326         ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
327         ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
328         ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
329         if (newHolder != null) {
330             // carry over translation values
331             resetAnimation(newHolder);
332             ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
333             ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
334             ViewCompat.setAlpha(newHolder.itemView, 0);
335         }
336         mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
337         return true;
338     }
339 
animateChangeImpl(final ChangeInfo changeInfo)340     private void animateChangeImpl(final ChangeInfo changeInfo) {
341         final ViewHolder holder = changeInfo.oldHolder;
342         final View view = holder == null ? null : holder.itemView;
343         final ViewHolder newHolder = changeInfo.newHolder;
344         final View newView = newHolder != null ? newHolder.itemView : null;
345         if (view != null) {
346             final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
347                     getChangeDuration());
348             mChangeAnimations.add(changeInfo.oldHolder);
349             oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
350             oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
351             oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
352                 @Override
353                 public void onAnimationStart(View view) {
354                     dispatchChangeStarting(changeInfo.oldHolder, true);
355                 }
356 
357                 @Override
358                 public void onAnimationEnd(View view) {
359                     oldViewAnim.setListener(null);
360                     ViewCompat.setAlpha(view, 1);
361                     ViewCompat.setTranslationX(view, 0);
362                     ViewCompat.setTranslationY(view, 0);
363                     dispatchChangeFinished(changeInfo.oldHolder, true);
364                     mChangeAnimations.remove(changeInfo.oldHolder);
365                     dispatchFinishedWhenDone();
366                 }
367             }).start();
368         }
369         if (newView != null) {
370             final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
371             mChangeAnimations.add(changeInfo.newHolder);
372             newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
373                     alpha(1).setListener(new VpaListenerAdapter() {
374                 @Override
375                 public void onAnimationStart(View view) {
376                     dispatchChangeStarting(changeInfo.newHolder, false);
377                 }
378                 @Override
379                 public void onAnimationEnd(View view) {
380                     newViewAnimation.setListener(null);
381                     ViewCompat.setAlpha(newView, 1);
382                     ViewCompat.setTranslationX(newView, 0);
383                     ViewCompat.setTranslationY(newView, 0);
384                     dispatchChangeFinished(changeInfo.newHolder, false);
385                     mChangeAnimations.remove(changeInfo.newHolder);
386                     dispatchFinishedWhenDone();
387                 }
388             }).start();
389         }
390     }
391 
endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item)392     private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
393         for (int i = infoList.size() - 1; i >= 0; i--) {
394             ChangeInfo changeInfo = infoList.get(i);
395             if (endChangeAnimationIfNecessary(changeInfo, item)) {
396                 if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
397                     infoList.remove(changeInfo);
398                 }
399             }
400         }
401     }
402 
endChangeAnimationIfNecessary(ChangeInfo changeInfo)403     private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
404         if (changeInfo.oldHolder != null) {
405             endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
406         }
407         if (changeInfo.newHolder != null) {
408             endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
409         }
410     }
endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item)411     private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
412         boolean oldItem = false;
413         if (changeInfo.newHolder == item) {
414             changeInfo.newHolder = null;
415         } else if (changeInfo.oldHolder == item) {
416             changeInfo.oldHolder = null;
417             oldItem = true;
418         } else {
419             return false;
420         }
421         ViewCompat.setAlpha(item.itemView, 1);
422         ViewCompat.setTranslationX(item.itemView, 0);
423         ViewCompat.setTranslationY(item.itemView, 0);
424         dispatchChangeFinished(item, oldItem);
425         return true;
426     }
427 
428     @Override
endAnimation(ViewHolder item)429     public void endAnimation(ViewHolder item) {
430         final View view = item.itemView;
431         // this will trigger end callback which should set properties to their target values.
432         ViewCompat.animate(view).cancel();
433         // TODO if some other animations are chained to end, how do we cancel them as well?
434         for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
435             MoveInfo moveInfo = mPendingMoves.get(i);
436             if (moveInfo.holder == item) {
437                 ViewCompat.setTranslationY(view, 0);
438                 ViewCompat.setTranslationX(view, 0);
439                 dispatchMoveFinished(item);
440                 mPendingMoves.remove(i);
441             }
442         }
443         endChangeAnimation(mPendingChanges, item);
444         if (mPendingRemovals.remove(item)) {
445             ViewCompat.setAlpha(view, 1);
446             dispatchRemoveFinished(item);
447         }
448         if (mPendingAdditions.remove(item)) {
449             ViewCompat.setAlpha(view, 1);
450             dispatchAddFinished(item);
451         }
452 
453         for (int i = mChangesList.size() - 1; i >= 0; i--) {
454             ArrayList<ChangeInfo> changes = mChangesList.get(i);
455             endChangeAnimation(changes, item);
456             if (changes.isEmpty()) {
457                 mChangesList.remove(i);
458             }
459         }
460         for (int i = mMovesList.size() - 1; i >= 0; i--) {
461             ArrayList<MoveInfo> moves = mMovesList.get(i);
462             for (int j = moves.size() - 1; j >= 0; j--) {
463                 MoveInfo moveInfo = moves.get(j);
464                 if (moveInfo.holder == item) {
465                     ViewCompat.setTranslationY(view, 0);
466                     ViewCompat.setTranslationX(view, 0);
467                     dispatchMoveFinished(item);
468                     moves.remove(j);
469                     if (moves.isEmpty()) {
470                         mMovesList.remove(i);
471                     }
472                     break;
473                 }
474             }
475         }
476         for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
477             ArrayList<ViewHolder> additions = mAdditionsList.get(i);
478             if (additions.remove(item)) {
479                 ViewCompat.setAlpha(view, 1);
480                 dispatchAddFinished(item);
481                 if (additions.isEmpty()) {
482                     mAdditionsList.remove(i);
483                 }
484             }
485         }
486 
487         // animations should be ended by the cancel above.
488         //noinspection PointlessBooleanExpression,ConstantConditions
489         if (mRemoveAnimations.remove(item) && DEBUG) {
490             throw new IllegalStateException("after animation is cancelled, item should not be in "
491                     + "mRemoveAnimations list");
492         }
493 
494         //noinspection PointlessBooleanExpression,ConstantConditions
495         if (mAddAnimations.remove(item) && DEBUG) {
496             throw new IllegalStateException("after animation is cancelled, item should not be in "
497                     + "mAddAnimations list");
498         }
499 
500         //noinspection PointlessBooleanExpression,ConstantConditions
501         if (mChangeAnimations.remove(item) && DEBUG) {
502             throw new IllegalStateException("after animation is cancelled, item should not be in "
503                     + "mChangeAnimations list");
504         }
505 
506         //noinspection PointlessBooleanExpression,ConstantConditions
507         if (mMoveAnimations.remove(item) && DEBUG) {
508             throw new IllegalStateException("after animation is cancelled, item should not be in "
509                     + "mMoveAnimations list");
510         }
511         dispatchFinishedWhenDone();
512     }
513 
resetAnimation(ViewHolder holder)514     private void resetAnimation(ViewHolder holder) {
515         AnimatorCompatHelper.clearInterpolator(holder.itemView);
516         endAnimation(holder);
517     }
518 
519     @Override
isRunning()520     public boolean isRunning() {
521         return (!mPendingAdditions.isEmpty() ||
522                 !mPendingChanges.isEmpty() ||
523                 !mPendingMoves.isEmpty() ||
524                 !mPendingRemovals.isEmpty() ||
525                 !mMoveAnimations.isEmpty() ||
526                 !mRemoveAnimations.isEmpty() ||
527                 !mAddAnimations.isEmpty() ||
528                 !mChangeAnimations.isEmpty() ||
529                 !mMovesList.isEmpty() ||
530                 !mAdditionsList.isEmpty() ||
531                 !mChangesList.isEmpty());
532     }
533 
534     /**
535      * Check the state of currently pending and running animations. If there are none
536      * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
537      * listeners.
538      */
dispatchFinishedWhenDone()539     private void dispatchFinishedWhenDone() {
540         if (!isRunning()) {
541             dispatchAnimationsFinished();
542         }
543     }
544 
545     @Override
endAnimations()546     public void endAnimations() {
547         int count = mPendingMoves.size();
548         for (int i = count - 1; i >= 0; i--) {
549             MoveInfo item = mPendingMoves.get(i);
550             View view = item.holder.itemView;
551             ViewCompat.setTranslationY(view, 0);
552             ViewCompat.setTranslationX(view, 0);
553             dispatchMoveFinished(item.holder);
554             mPendingMoves.remove(i);
555         }
556         count = mPendingRemovals.size();
557         for (int i = count - 1; i >= 0; i--) {
558             ViewHolder item = mPendingRemovals.get(i);
559             dispatchRemoveFinished(item);
560             mPendingRemovals.remove(i);
561         }
562         count = mPendingAdditions.size();
563         for (int i = count - 1; i >= 0; i--) {
564             ViewHolder item = mPendingAdditions.get(i);
565             View view = item.itemView;
566             ViewCompat.setAlpha(view, 1);
567             dispatchAddFinished(item);
568             mPendingAdditions.remove(i);
569         }
570         count = mPendingChanges.size();
571         for (int i = count - 1; i >= 0; i--) {
572             endChangeAnimationIfNecessary(mPendingChanges.get(i));
573         }
574         mPendingChanges.clear();
575         if (!isRunning()) {
576             return;
577         }
578 
579         int listCount = mMovesList.size();
580         for (int i = listCount - 1; i >= 0; i--) {
581             ArrayList<MoveInfo> moves = mMovesList.get(i);
582             count = moves.size();
583             for (int j = count - 1; j >= 0; j--) {
584                 MoveInfo moveInfo = moves.get(j);
585                 ViewHolder item = moveInfo.holder;
586                 View view = item.itemView;
587                 ViewCompat.setTranslationY(view, 0);
588                 ViewCompat.setTranslationX(view, 0);
589                 dispatchMoveFinished(moveInfo.holder);
590                 moves.remove(j);
591                 if (moves.isEmpty()) {
592                     mMovesList.remove(moves);
593                 }
594             }
595         }
596         listCount = mAdditionsList.size();
597         for (int i = listCount - 1; i >= 0; i--) {
598             ArrayList<ViewHolder> additions = mAdditionsList.get(i);
599             count = additions.size();
600             for (int j = count - 1; j >= 0; j--) {
601                 ViewHolder item = additions.get(j);
602                 View view = item.itemView;
603                 ViewCompat.setAlpha(view, 1);
604                 dispatchAddFinished(item);
605                 additions.remove(j);
606                 if (additions.isEmpty()) {
607                     mAdditionsList.remove(additions);
608                 }
609             }
610         }
611         listCount = mChangesList.size();
612         for (int i = listCount - 1; i >= 0; i--) {
613             ArrayList<ChangeInfo> changes = mChangesList.get(i);
614             count = changes.size();
615             for (int j = count - 1; j >= 0; j--) {
616                 endChangeAnimationIfNecessary(changes.get(j));
617                 if (changes.isEmpty()) {
618                     mChangesList.remove(changes);
619                 }
620             }
621         }
622 
623         cancelAll(mRemoveAnimations);
624         cancelAll(mMoveAnimations);
625         cancelAll(mAddAnimations);
626         cancelAll(mChangeAnimations);
627 
628         dispatchAnimationsFinished();
629     }
630 
cancelAll(List<ViewHolder> viewHolders)631     void cancelAll(List<ViewHolder> viewHolders) {
632         for (int i = viewHolders.size() - 1; i >= 0; i--) {
633             ViewCompat.animate(viewHolders.get(i).itemView).cancel();
634         }
635     }
636 
637     /**
638      * {@inheritDoc}
639      * <p>
640      * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
641      * When this is the case:
642      * <ul>
643      * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
644      * ViewHolder arguments will be the same instance.
645      * </li>
646      * <li>
647      * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
648      * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
649      * run a move animation instead.
650      * </li>
651      * </ul>
652      */
653     @Override
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)654     public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
655             @NonNull List<Object> payloads) {
656         return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
657     }
658 
659     private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
660         @Override
onAnimationStart(View view)661         public void onAnimationStart(View view) {}
662 
663         @Override
onAnimationEnd(View view)664         public void onAnimationEnd(View view) {}
665 
666         @Override
onAnimationCancel(View view)667         public void onAnimationCancel(View view) {}
668     }
669 }
670