From d9a82221a457fe4d4e9dda73bb5680024e704e92 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Thu, 4 Apr 2019 09:42:53 -0700 Subject: [PATCH] Back out "[react-native] Remove experimental gating for LayoutAnimation on Android" Summary: We've identified a couple of remaining issues that need to be re-tested before we can ship this more broadly. Reviewed By: fred2028 Differential Revision: D14775730 fbshipit-source-id: 22402149066c5fbe72c36fcf7f547d63feaf5241 --- Libraries/LayoutAnimation/LayoutAnimation.js | 4 ++++ Libraries/ReactNative/UIManager.js | 9 --------- RNTester/js/RNTesterApp.android.js | 3 +++ .../uimanager/NativeViewHierarchyManager.java | 14 +++++++++++--- .../react/uimanager/UIImplementation.java | 15 +++++++++++++++ .../react/uimanager/UIManagerModule.java | 16 ++++++++++++++++ .../react/uimanager/UIViewOperationQueue.java | 18 ++++++++++++++++++ 7 files changed, 67 insertions(+), 12 deletions(-) diff --git a/Libraries/LayoutAnimation/LayoutAnimation.js b/Libraries/LayoutAnimation/LayoutAnimation.js index 74b3eb103085f1..ef92d99cec6319 100644 --- a/Libraries/LayoutAnimation/LayoutAnimation.js +++ b/Libraries/LayoutAnimation/LayoutAnimation.js @@ -90,6 +90,10 @@ const Presets = { * next layout happens. * * A common way to use this API is to call it before calling `setState`. + * + * Note that in order to get this to work on **Android** you need to set the following flags via `UIManager`: + * + * UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true); */ const LayoutAnimation = { /** diff --git a/Libraries/ReactNative/UIManager.js b/Libraries/ReactNative/UIManager.js index dfbece03f913d4..833005dbe92024 100644 --- a/Libraries/ReactNative/UIManager.js +++ b/Libraries/ReactNative/UIManager.js @@ -100,15 +100,6 @@ function lazifyViewManagerConfig(viewName) { } } -if (Platform.OS === 'android') { - UIManager.setLayoutAnimationEnabledExperimental = () => { - console.warn( - 'setLayoutAnimationEnabledExperimental is deprecated. ' + - 'Layout animations are enabled by default', - ); - }; -} - /** * Copies the ViewManager constants and commands into UIManager. This is * only needed for iOS, which puts the constants in the ViewManager diff --git a/RNTester/js/RNTesterApp.android.js b/RNTester/js/RNTesterApp.android.js index 1f8e00a40ccfdf..16c6660e0742e9 100644 --- a/RNTester/js/RNTesterApp.android.js +++ b/RNTester/js/RNTesterApp.android.js @@ -27,6 +27,7 @@ const RNTesterExampleList = require('./RNTesterExampleList'); * making Flow check .android.js files. */ const RNTesterList = require('./RNTesterList'); const RNTesterNavigationReducer = require('./RNTesterNavigationReducer'); +const UIManager = require('UIManager'); const URIActionMap = require('./URIActionMap'); const View = require('View'); @@ -34,6 +35,8 @@ const nativeImageSource = require('nativeImageSource'); import type {RNTesterNavigationState} from './RNTesterNavigationReducer'; +UIManager.setLayoutAnimationEnabledExperimental(true); + const DRAWER_WIDTH_LEFT = 56; type Props = { 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 469dbb7c19ac9b..f5953bacdc45b5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -74,6 +74,7 @@ public class NativeViewHierarchyManager { private final LayoutAnimationController mLayoutAnimator = new LayoutAnimationController(); private final Map mTagsToPendingIndicesToDelete = new HashMap<>(); + private boolean mLayoutAnimationEnabled; private PopupMenu mPopupMenu; public NativeViewHierarchyManager(ViewManagerRegistry viewManagers) { @@ -105,6 +106,10 @@ public synchronized final ViewManager resolveViewManager(int tag) { return viewManager; } + public void setLayoutAnimationEnabled(boolean enabled) { + mLayoutAnimationEnabled = enabled; + } + public synchronized void updateInstanceHandle(int tag, long instanceHandle) { UiThreadUtil.assertOnUiThread(); @@ -220,7 +225,8 @@ public long getInstanceHandle(int reactTag) { } private void updateLayout(View viewToUpdate, int x, int y, int width, int height) { - if (mLayoutAnimator.shouldAnimateLayout(viewToUpdate)) { + if (mLayoutAnimationEnabled && + mLayoutAnimator.shouldAnimateLayout(viewToUpdate)) { mLayoutAnimator.applyLayoutUpdate(viewToUpdate, x, y, width, height); } else { viewToUpdate.layout(x, y, x + width, y + height); @@ -427,7 +433,8 @@ public synchronized void manageChildren( int normalizedIndexToRemove = normalizeIndex(indexToRemove, pendingIndicesToDelete); View viewToRemove = viewManager.getChildAt(viewToManage, normalizedIndexToRemove); - if (mLayoutAnimator.shouldAnimateLayout(viewToRemove) && + if (mLayoutAnimationEnabled && + mLayoutAnimator.shouldAnimateLayout(viewToRemove) && arrayContains(tagsToDelete, viewToRemove.getId())) { // The view will be removed and dropped by the 'delete' layout animation // instead, so do nothing @@ -476,7 +483,8 @@ public synchronized void manageChildren( tagsToDelete)); } - if (mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) { + if (mLayoutAnimationEnabled && + mLayoutAnimator.shouldAnimateLayout(viewToDestroy)) { int updatedCount = pendingIndicesToDelete.get(indexToDelete, 0) + 1; pendingIndicesToDelete.put(indexToDelete, updatedCount); mLayoutAnimator.deleteView( diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 36c19b70d9977c..dfa310b76d4e26 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -693,6 +693,21 @@ protected void updateViewHierarchy() { } } + /** + * LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled + * explicitly in order to avoid regression in existing application written for iOS using this API. + * + * Warning : This method will be removed in future version of React Native, and layout animation + * will be enabled by default, so always check for its existence before invoking it. + * + * TODO(9139831) : remove this method once layout animation is fully stable. + * + * @param enabled whether layout animation is enabled or not + */ + public void setLayoutAnimationEnabledExperimental(boolean enabled) { + mOperationsQueue.enqueueSetLayoutAnimationEnabled(enabled); + } + /** * Configure an animation to be used for the native layout changes, and native views * creation. The animation will only apply during the current batch operations. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 73a8cda51e8be7..51a229f73ae72e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -689,6 +689,22 @@ public void dismissPopupMenu() { mUIImplementation.dismissPopupMenu(); } + /** + * LayoutAnimation API on Android is currently experimental. Therefore, it needs to be enabled + * explicitly in order to avoid regression in existing application written for iOS using this API. + * + *

Warning : This method will be removed in future version of React Native, and layout + * animation will be enabled by default, so always check for its existence before invoking it. + * + *

TODO(9139831) : remove this method once layout animation is fully stable. + * + * @param enabled whether layout animation is enabled or not + */ + @ReactMethod + public void setLayoutAnimationEnabledExperimental(boolean enabled) { + mUIImplementation.setLayoutAnimationEnabledExperimental(enabled); + } + /** * Configure an animation to be used for the native layout changes, and native views creation. The * animation will only apply during the current batch operations. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index 2c87a999dda01a..cf66d2a5c50f40 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -354,6 +354,19 @@ public AnimationOperation(int animationID) { } } + private class SetLayoutAnimationEnabledOperation implements UIOperation { + private final boolean mEnabled; + + private SetLayoutAnimationEnabledOperation(final boolean enabled) { + mEnabled = enabled; + } + + @Override + public void execute() { + mNativeViewHierarchyManager.setLayoutAnimationEnabled(mEnabled); + } + } + private class ConfigureLayoutAnimationOperation implements UIOperation { private final ReadableMap mConfig; private final Callback mAnimationComplete; @@ -723,6 +736,11 @@ public void enqueueSetChildren( new SetChildrenOperation(reactTag, childrenTags)); } + public void enqueueSetLayoutAnimationEnabled( + final boolean enabled) { + mOperations.add(new SetLayoutAnimationEnabledOperation(enabled)); + } + public void enqueueConfigureLayoutAnimation( final ReadableMap config, final Callback onAnimationComplete) {