Layout Animation fix for normalized indices
Summary: [Android][Fix] - Fix how we normalize indices Before, we were incorrectly normalizing indices given pending view deletion in the view hierarchy (namely, using LayoutAnimations) What we had before (that was wrong): * Maintained a pendingIndices sparse array for each tag * For each pendingIndices sparse array we'd keep track of how many views we deleted at each abstract index * Given an abstract index to delete a view at, we'd consult `pendingIndices` array to sum how many pending deletes we had for indices equal or smaller than and add to abstract index ^ Above algorithm is wrong and you can follow along with the following example to see how. ## The correct approach Given these operations in this order: 1. {tagsToDelete: [123], indicesToDelete [2]} 2. {tagsToDelete: [124], indicesToDelete [1]} 3. {tagsToDelete: [125], indicesToDelete [2]} 4. {tagsToDelete: [126], indicesToDelete [1]} The approach we want to be using to calculate normalized indices: ### Step 1: Delete tag 124 at index 2 |Views:|122|123|124|125|126|127| |Actual Indices:|0|1|2|3|4|5| |Abstract Indices:|0|1|2|3|4|5| => simple, we just mark the view at 2 ### Step 2: Delete tag 123 at index 1 View tags and indices: |Views|122|123|~~124~~|125|126|127| |Actual indices|0|1|~~2~~|3|4|5| |Abstract Indices|0|1||2|3|4| => again, simple, we can just use the normalized index 1 because no pending deletes affect this operation ### Step 3: Delete tag 126 at index 2 View tags and indices: |Views|122|~~123~~|~~124~~|125|126|127| |Actual Indices|0|~~1~~|~~2~~|3|4|5| |Abstract Indices|0|||1|2|3| => Here we want to normalize this index to 4 because we need to account the 2 views that should be skipped ### Step 4: Delete tag 125 at index 1 View tags and indices: |Views|122|~~123~~|~~124~~|125|~~126~~|127| |Actual Indices|0|~~1~~|~~2~~|3|~~4~~|5| |Abstract Indices|0|||1||2| => The normalized index should be 3. This diff updates the function `normalizeIndex` to do the above algorithm by repurposing `pendingIndicesToDelete` to instead be a sparse int array that holds [normalizedIndex]=[tag] pairs It's required that `pendingIndicesToDelete` is ordered by the normalizedIndex. Reviewed By: mdvacca Differential Revision: D15485132 fbshipit-source-id: 43e57dffa807e8ea50fa1650c5dec13a6fded624
This commit is contained in:
Родитель
5979eafb16
Коммит
bd3023abea
|
@ -338,17 +338,52 @@ public class NativeViewHierarchyManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given an index to action on under synchronous deletes, return an updated index factoring in
|
||||
* asynchronous deletes (where the async delete operations have not yet been performed)
|
||||
* Given an index, normalize against pending view deletion indices in the native view hierarchy
|
||||
* @param index the index in the view operation under the assumption all view operations are synchronous
|
||||
* @param pendingIndicesToDelete sparse array of view tags at normalized indices
|
||||
*/
|
||||
private int normalizeIndex(int index, SparseIntArray pendingIndices) {
|
||||
int normalizedIndex = index;
|
||||
for (int i = 0; i <= index; i++) {
|
||||
normalizedIndex += pendingIndices.get(i);
|
||||
private int normalizeIndex(int index, SparseIntArray pendingIndicesToDelete) {
|
||||
int normalizedIndex = -1;
|
||||
while (index >= 0) {
|
||||
normalizedIndex += 1;
|
||||
if (pendingIndicesToDelete.get(normalizedIndex, -1) == -1) { // assuming we never have negative tag
|
||||
index--;
|
||||
}
|
||||
}
|
||||
return normalizedIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add view tag to pendingIndicesToDelete. Views in pendingIndicesToDelete are marked for deletion but have not been deleted yet
|
||||
* @param index the index in the view to be deleted as provided by the view operation
|
||||
* @param tag the view tag
|
||||
* @param pendingIndicesToDelete sparse array of normalizedIndices to view tags marked for deletion
|
||||
*/
|
||||
private void addPendingIndex(int index, int tag, SparseIntArray pendingIndicesToDelete) {
|
||||
int normalizedIndex = normalizeIndex(index, pendingIndicesToDelete);
|
||||
if (pendingIndicesToDelete.get(normalizedIndex) > 0) {
|
||||
throw new IllegalViewOperationException("Invalid!!");
|
||||
}
|
||||
pendingIndicesToDelete.put(normalizedIndex, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* When delete is completed, remove view from pendingIndicesToDelete
|
||||
* @param tag tag of the view to be removed
|
||||
* @param pendingIndicesToDelete sparse array of normalizedIndices to view tags marked for deletion
|
||||
*/
|
||||
private void removePendingIndex(int tag, SparseIntArray pendingIndicesToDelete) {
|
||||
// indexAt refers to index within pendingIndicesToDelete sparse array, not the normalized index
|
||||
int indexAt = pendingIndicesToDelete.indexOfValue(tag);
|
||||
pendingIndicesToDelete.removeAt(indexAt);
|
||||
for (indexAt = indexAt + 1; indexAt < pendingIndicesToDelete.size(); indexAt ++) {
|
||||
int nextTag = pendingIndicesToDelete.valueAt(indexAt);
|
||||
int nextKey = pendingIndicesToDelete.keyAt(indexAt);
|
||||
pendingIndicesToDelete.removeAt(indexAt);
|
||||
pendingIndicesToDelete.put(nextKey - 1, nextTag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given React tag, return sparse array of direct child indices that are pending deletion (due to
|
||||
* async view deletion)
|
||||
|
@ -471,8 +506,8 @@ public class NativeViewHierarchyManager {
|
|||
|
||||
if (mLayoutAnimationEnabled &&
|
||||
mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) {
|
||||
int updatedCount = pendingIndicesToDelete.get(indexToDelete, 0) + 1;
|
||||
pendingIndicesToDelete.put(indexToDelete, updatedCount);
|
||||
|
||||
addPendingIndex(indexToDelete, tagToDelete, pendingIndicesToDelete);
|
||||
mLayoutAnimator.deleteView(
|
||||
viewToDestroy,
|
||||
new LayoutAnimationListener() {
|
||||
|
@ -481,8 +516,7 @@ public class NativeViewHierarchyManager {
|
|||
viewManager.removeView(viewToManage, viewToDestroy);
|
||||
dropView(viewToDestroy);
|
||||
|
||||
int count = pendingIndicesToDelete.get(indexToDelete, 0);
|
||||
pendingIndicesToDelete.put(indexToDelete, Math.max(0, count - 1));
|
||||
removePendingIndex(viewToDestroy.getId(), pendingIndicesToDelete);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
Загрузка…
Ссылка в новой задаче