1717import com .facebook .react .bridge .Callback ;
1818import com .facebook .react .bridge .ReadableMap ;
1919import com .facebook .react .bridge .UiThreadUtil ;
20+ import java .util .ArrayList ;
2021import javax .annotation .concurrent .NotThreadSafe ;
2122
2223/**
@@ -31,6 +32,8 @@ public class LayoutAnimationController {
3132 private final AbstractLayoutAnimation mLayoutUpdateAnimation = new LayoutUpdateAnimation ();
3233 private final AbstractLayoutAnimation mLayoutDeleteAnimation = new LayoutDeleteAnimation ();
3334 private final SparseArray <LayoutHandlingAnimation > mLayoutHandlers = new SparseArray <>(0 );
35+ private final SparseArray <ArrayList <Animation >> mDeleteAnimationsByParentTag =
36+ new SparseArray <>();
3437
3538 private boolean mShouldAnimateLayout ;
3639 private long mMaxAnimationDuration = -1 ;
@@ -113,6 +116,7 @@ public void applyLayoutUpdate(View view, int x, int y, int width, int height) {
113116
114117 // Update an ongoing animation if possible, otherwise the layout update would be ignored as
115118 // the existing animation would still animate to the old layout.
119+ // Note the view is already inserted into the view hierarchy.
116120 LayoutHandlingAnimation existingAnimation = mLayoutHandlers .get (reactTag );
117121 if (existingAnimation != null ) {
118122 existingAnimation .onLayoutUpdate (x , y , width , height );
@@ -164,11 +168,14 @@ public void onAnimationRepeat(Animation animation) {}
164168 * Animate a view deletion using the layout animation configuration supplied during
165169 * initialization.
166170 *
171+ * @param parentReactTag tag of parent view of @param view. used to associate animation with for
172+ * canceling
167173 * @param view The view to animate.
168174 * @param listener Called once the animation is finished, should be used to completely remove the
169175 * view.
170176 */
171- public void deleteView (final View view , final LayoutAnimationListener listener ) {
177+ public void deleteView (
178+ final int parentReactTag , final View view , final LayoutAnimationListener listener ) {
172179 UiThreadUtil .assertOnUiThread ();
173180
174181 Animation animation =
@@ -188,6 +195,10 @@ public void onAnimationRepeat(Animation anim) {}
188195
189196 @ Override
190197 public void onAnimationEnd (Animation anim ) {
198+ ArrayList <Animation > animations = mDeleteAnimationsByParentTag .get (parentReactTag );
199+ if (animations != null ) {
200+ animations .remove (anim );
201+ }
191202 listener .onAnimationEnd ();
192203 }
193204 });
@@ -198,7 +209,16 @@ public void onAnimationEnd(Animation anim) {
198209 mMaxAnimationDuration = animationDuration ;
199210 }
200211
212+ // Update our tracking list of delete animations
213+ ArrayList <Animation > deleteAnimations = mDeleteAnimationsByParentTag .get (parentReactTag );
214+ if (deleteAnimations == null ) {
215+ deleteAnimations = new ArrayList <>();
216+ mDeleteAnimationsByParentTag .put (parentReactTag , deleteAnimations );
217+ }
218+ deleteAnimations .add (animation );
219+
201220 view .startAnimation (animation );
221+
202222 } else {
203223 listener .onAnimationEnd ();
204224 }
@@ -225,4 +245,23 @@ private void scheduleCompletionCallback(long delayMillis) {
225245 sCompletionHandler .postDelayed (mCompletionRunnable , delayMillis );
226246 }
227247 }
248+
249+ /**
250+ * Animate a view deletion using the layout animation configuration supplied during
251+ * initialization.
252+ *
253+ * @param viewTag tag of parent view that we're going to cancel all child animations.
254+ */
255+ public void cancelAnimationsForViewTag (int viewTag ) {
256+ ArrayList <Animation > animations = mDeleteAnimationsByParentTag .get (viewTag );
257+ if (animations == null ) {
258+ return ;
259+ }
260+
261+ for (int i = 0 ; i < animations .size (); i ++) {
262+ animations .get (i ).cancel ();
263+ }
264+
265+ mDeleteAnimationsByParentTag .remove (viewTag );
266+ }
228267}
0 commit comments