diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java index ce7520ee9bd942..76749aaff7f948 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java @@ -295,6 +295,16 @@ public void didScheduleMountItems(UIManager uiManager) { mCurrentFrameNumber++; } + @Override + public void willMountItems(UIManager uiManager) { + // noop + } + + @Override + public void didMountItems(UIManager uiManager) { + // noop + } + // For FabricUIManager only @Override @UiThread diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java index 984bcf15848ee8..505999b3d5af33 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java @@ -12,10 +12,33 @@ public interface UIManagerListener { /** * Called right before view updates are dispatched at the end of a batch. This is useful if a * module needs to add UIBlocks to the queue before it is flushed. + * + *

This is called by Paper only. */ void willDispatchViewUpdates(UIManager uiManager); - /* Called right after view updates are dispatched for a frame. */ + /** + * Called on UIThread right before view updates are executed. + * + *

This is called by Fabric only. + */ + void willMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are executed. + * + *

This is called by Fabric only. + */ + void didMountItems(UIManager uiManager); + /** + * Called on UIThread right after view updates are dispatched for a frame. Note that this will be + * called for every frame even if there are no updates. + * + *

This is called by Fabric only. + */ void didDispatchMountItems(UIManager uiManager); - /* Called right after scheduleMountItems is called in Fabric, after a new tree is committed. */ + /** + * Called right after scheduleMountItems is called in Fabric, after a new tree is committed. + * + *

This is called by Fabric only. + */ void didScheduleMountItems(UIManager uiManager); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 0da8e55dd8d938..8e50e91a64d52e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -1171,6 +1171,20 @@ public Map getPerformanceCounters() { } private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener { + @Override + public void willMountItems() { + for (UIManagerListener listener : mListeners) { + listener.willMountItems(FabricUIManager.this); + } + } + + @Override + public void didMountItems() { + for (UIManagerListener listener : mListeners) { + listener.didMountItems(FabricUIManager.this); + } + } + @Override public void didDispatchMountItems() { for (UIManagerListener listener : mListeners) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java index 36abe8dd9f15cf..6861961c472640 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java @@ -192,6 +192,8 @@ private boolean dispatchMountItems() { return false; } + mItemDispatchListener.willMountItems(); + // As an optimization, execute all ViewCommands first // This should be: // 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always @@ -298,6 +300,9 @@ private boolean dispatchMountItems() { } mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime; } + + mItemDispatchListener.didMountItems(); + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); return true; @@ -414,6 +419,10 @@ private static void printMountItem(MountItem mountItem, String prefix) { } public interface ItemDispatchListener { + void willMountItems(); + + void didMountItems(); + void didDispatchMountItems(); } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java index 6ce4d8b23d2bbf..f76c432b59047c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java @@ -18,6 +18,7 @@ import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll; import com.facebook.react.views.view.ReactViewGroup; @@ -89,6 +90,14 @@ public void stop() { * been updated. */ public void updateScrollPosition() { + // On Fabric this will be called internally in `didMountItems`. + if (ViewUtil.getUIManagerType(mScrollView.getId()) == UIManagerType.FABRIC) { + return; + } + updateScrollPositionInternal(); + } + + private void updateScrollPositionInternal() { if (mConfig == null || mFirstVisibleView == null || mPrevFirstVisibleFrame == null) { return; } @@ -169,6 +178,16 @@ public void run() { }); } + @Override + public void willMountItems(UIManager uiManager) { + computeTargetView(); + } + + @Override + public void didMountItems(UIManager uiManager) { + updateScrollPositionInternal(); + } + @Override public void didDispatchMountItems(UIManager uiManager) { // noop