From c938c0afbfbd0e5ba8853feb212a976f605f51f0 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Mon, 9 Mar 2020 18:25:18 -0700 Subject: [PATCH] Adjust Layout Animation with pending deletion set Summary: Changelog: [Internal] This solves the problem of viewToAdd indices being invalid if the deletes are asynchronous within the scope of one `manageChildren` call. Since deletions are performed first, we keep a set of tags being deleted. During view insertion, we iterate through and filter those tags out of our count. Reviewed By: JoshuaGross Differential Revision: D20324643 fbshipit-source-id: 150230428fcd65b8c43cc1f2331e9ce02d31fff9 --- .../uimanager/NativeViewHierarchyManager.java | 24 ++++++++++++++++++- 1 file changed, 23 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 93ccf116e3e..55208453fc7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -35,6 +35,8 @@ import com.facebook.react.uimanager.layoutanimation.LayoutAnimationListener; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; /** @@ -363,6 +365,7 @@ public class NativeViewHierarchyManager { @Nullable int[] tagsToDelete) { UiThreadUtil.assertOnUiThread(); + final Set pendingDeletionTags = new HashSet<>(); mLayoutAnimator.cancelAnimationsForViewTag(tag); final ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag); @@ -446,6 +449,7 @@ public class NativeViewHierarchyManager { } if (mLayoutAnimationEnabled && mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) { + pendingDeletionTags.add(tagToDelete); mLayoutAnimator.deleteView( tag, viewToDestroy, @@ -458,6 +462,7 @@ public class NativeViewHierarchyManager { viewManager.removeView(viewToManage, viewToDestroy); dropView(viewToDestroy); + pendingDeletionTags.remove(viewToDestroy.getId()); } }); } else { @@ -478,7 +483,24 @@ public class NativeViewHierarchyManager { + constructManageChildrenErrorMessage( viewToManage, viewManager, indicesToRemove, viewsToAdd, tagsToDelete)); } - viewManager.addView(viewToManage, viewToAdd, viewAtIndex.mIndex); + + int normalizedIndex = viewAtIndex.mIndex; + if (!pendingDeletionTags.isEmpty()) { + normalizedIndex = 0; + int counter = 0; + while (normalizedIndex < viewToManage.getChildCount()) { + if (counter == viewAtIndex.mIndex) { + break; + } + View v = viewToManage.getChildAt(normalizedIndex); + if (!pendingDeletionTags.contains(v.getId())) { + counter++; + } + normalizedIndex++; + } + } + + viewManager.addView(viewToManage, viewToAdd, normalizedIndex); } } }