From 911e1384c35cf8cbc19d348df16415b199a94932 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Mon, 6 Jun 2016 19:25:45 -0400 Subject: [PATCH] Fix LayoutAnimation Android bug when adding and removing views at the same time --- .../uimanager/NativeViewHierarchyManager.java | 17 +++++++- .../LayoutAnimationController.java | 40 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index 36ab83c8fa871f..d7728cdc524a6a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -352,6 +352,7 @@ public void manageChildren( arrayContains(tagsToDelete, viewToRemove.getId())) { // The view will be removed and dropped by the 'delete' layout animation // instead, so do nothing + mLayoutAnimator.addViewAnimatingDeletion(viewToRemove); } else { viewManager.removeViewAt(viewToManage, indexToRemove); } @@ -375,7 +376,20 @@ public void manageChildren( viewsToAdd, tagsToDelete)); } - viewManager.addView(viewToManage, viewToAdd, viewAtIndex.mIndex); + + // When performing layout delete animations views are not removed immediately + // from their container so we need to offset the insert index if a view + // that is going to be removed is before the view we want to insert. + int addViewIndex = viewAtIndex.mIndex; + if (mLayoutAnimator.isAnimatingViewDeletion()) { + for(int index = 0; index < viewAtIndex.mIndex; index++) { + if (mLayoutAnimator.isViewAnimatingDeletion(viewManager.getChildAt(viewToManage, index))) { + addViewIndex++; + } + } + } + + viewManager.addView(viewToManage, viewToAdd, addViewIndex); } } @@ -399,6 +413,7 @@ public void manageChildren( mLayoutAnimator.deleteView(viewToDestroy, new LayoutAnimationListener() { @Override public void onAnimationEnd() { + mLayoutAnimator.removeViewAnimatingDeletion(viewToDestroy); viewManager.removeView(viewToManage, viewToDestroy); dropView(viewToDestroy); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.java index d5a08e6b264ac5..29dbfdc3522700 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/layoutanimation/LayoutAnimationController.java @@ -2,6 +2,9 @@ package com.facebook.react.uimanager.layoutanimation; +import java.util.ArrayList; +import java.util.List; + import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; @@ -29,6 +32,8 @@ public class LayoutAnimationController { private final AbstractLayoutAnimation mLayoutDeleteAnimation = new LayoutDeleteAnimation(); private boolean mShouldAnimateLayout; + private List mDeleteAnimatedViews = new ArrayList<>(); + public void initializeFromConfig(final @Nullable ReadableMap config) { if (!ENABLED) { return; @@ -138,6 +143,41 @@ public void onAnimationEnd(Animation anim) { } } + /** + * Add a view to list of views that are being deleted with an animation. + * + * @param view View to add + */ + public void addViewAnimatingDeletion(View view) { + mDeleteAnimatedViews.add(view); + } + + /** + * Remove a view to list of views that are being deleted with an animation. + * + * @param view View to remove + */ + public void removeViewAnimatingDeletion(View view) { + mDeleteAnimatedViews.remove(view); + } + + /** + * Returns if the view is being deleted with an animation. + * + * @param view View to check + * @return If it is being animated + */ + public boolean isViewAnimatingDeletion(View view) { + return mDeleteAnimatedViews.contains(view); + } + + /** + * Returns if there are any view being deleted with an animation. + */ + public boolean isAnimatingViewDeletion() { + return mDeleteAnimatedViews.size() > 0; + } + /** * Disables user interactions for a view and all it's subviews. */