From d825a4d712a1ba53c0f4209c9da5d51578209a6d Mon Sep 17 00:00:00 2001 From: Thomas Nardone Date: Thu, 24 Oct 2024 11:05:38 -0700 Subject: [PATCH] Add OnLayoutChange API for scroll views (#47177) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/47177 Changelog: [Android][Added] Add OnLayoutChange API for scroll views Reviewed By: lyahdav, mdvacca Differential Revision: D64843826 fbshipit-source-id: 3161730ccd0b3816d1704d07a02714a2ff04ae3a --- .../ReactAndroid/api/ReactAndroid.api | 7 ++++++ .../scroll/ReactHorizontalScrollView.java | 1 + .../react/views/scroll/ReactScrollView.java | 2 ++ .../views/scroll/ReactScrollViewHelper.kt | 22 +++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 0301eae8c91541..fba2db758a6bf5 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -7248,7 +7248,9 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper { public static final field SNAP_ALIGNMENT_DISABLED I public static final field SNAP_ALIGNMENT_END I public static final field SNAP_ALIGNMENT_START I + public static final fun addLayoutChangeListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener;)V public static final fun addScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V + public static final fun emitLayoutChangeEvent (Landroid/view/ViewGroup;)V public static final fun emitLayoutEvent (Landroid/view/ViewGroup;)V public static final fun emitScrollBeginDragEvent (Landroid/view/ViewGroup;)V public static final fun emitScrollEndDragEvent (Landroid/view/ViewGroup;FF)V @@ -7262,6 +7264,7 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper { public static final fun parseSnapToAlignment (Ljava/lang/String;)I public static final fun predictFinalScrollPosition (Landroid/view/ViewGroup;IIII)Landroid/graphics/Point; public final fun registerFlingAnimator (Landroid/view/ViewGroup;)V + public static final fun removeLayoutChangeListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener;)V public static final fun removeScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V public static final fun smoothScrollTo (Landroid/view/ViewGroup;II)V public static final fun updateFabricScrollState (Landroid/view/ViewGroup;)V @@ -7296,6 +7299,10 @@ public abstract interface class com/facebook/react/views/scroll/ReactScrollViewH public abstract fun getStateWrapper ()Lcom/facebook/react/uimanager/StateWrapper; } +public abstract interface class com/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener { + public abstract fun onLayoutChange (Landroid/view/ViewGroup;)V +} + public final class com/facebook/react/views/scroll/ReactScrollViewHelper$ReactScrollViewScrollState { public fun (I)V public final fun getDecelerationRate ()F diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 919777a11fd068..279868be6b4967 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -1444,6 +1444,7 @@ public void onLayoutChange( } else if (mMaintainVisibleContentPositionHelper != null) { mMaintainVisibleContentPositionHelper.updateScrollPosition(); } + ReactScrollViewHelper.emitLayoutChangeEvent(this); } /** diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 3d18ba1986b103..7203f62ac8b8ad 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -1246,6 +1246,8 @@ public void onLayoutChange( scrollTo(getScrollX(), maxScrollY); } } + + ReactScrollViewHelper.emitLayoutChangeEvent(this); } @Override diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt index 5877312d0e39a8..39867529645f90 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt @@ -47,6 +47,7 @@ public object ReactScrollViewHelper { // Support global native listeners for scroll events private val scrollListeners = CopyOnWriteArrayList>() + private val layoutChangeListeners = CopyOnWriteArrayList>() // If all else fails, this is the hardcoded value in OverScroller.java, in AOSP. // The default is defined here (as of this diff): @@ -151,6 +152,13 @@ public object ReactScrollViewHelper { } } + @JvmStatic + public fun emitLayoutChangeEvent(scrollView: ViewGroup) { + for (listener in layoutChangeListeners) { + listener.get()?.onLayoutChange(scrollView) + } + } + @JvmStatic public fun parseOverScrollMode(jsOverScrollMode: String?): Int { return if (jsOverScrollMode == null || jsOverScrollMode == AUTO) { @@ -214,6 +222,16 @@ public object ReactScrollViewHelper { scrollListeners.remove(WeakReference(listener)) } + @JvmStatic + public fun addLayoutChangeListener(listener: LayoutChangeListener) { + layoutChangeListeners.add(WeakReference(listener)) + } + + @JvmStatic + public fun removeLayoutChangeListener(listener: LayoutChangeListener) { + layoutChangeListeners.remove(WeakReference(listener)) + } + /** * Scroll the given view to the location (x, y), with provided initial velocity. This method works * by calculate the "would be" initial velocity with internal friction to move to the point (x, @@ -456,6 +474,10 @@ public object ReactScrollViewHelper { public fun onLayout(scrollView: ViewGroup?) } + public interface LayoutChangeListener { + public fun onLayoutChange(scrollView: ViewGroup) + } + public interface HasStateWrapper { public val stateWrapper: StateWrapper? }