From 9e65ba2b7b9afbd36d7ea7d422c2b46427913aa1 Mon Sep 17 00:00:00 2001 From: Genki Kondo Date: Mon, 30 Jan 2023 11:55:13 -0800 Subject: [PATCH] In onLayout, only scroll if the content view is ready Summary: ScrollViews don't properly maintain position where they are hidden and shown. There is an edge case where on onLayout for a ScrollView, its content may not have been laid out yet. This happens in some cases when a scroll view is hidden via display: 'none' (resulting in setVisibility(INVISIBLE)). Check that the content view is laid out before attempting a scroll. Changelog: [Internal][Fixed] - In onLayout, only scroll if the content view is ready Reviewed By: sshic Differential Revision: D42794750 fbshipit-source-id: 654a380bcae306da2704d3e190423c8de125833d --- .../scroll/ReactHorizontalScrollView.java | 19 +++++++++++-------- .../react/views/scroll/ReactScrollView.java | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 5f734256571277..c5271853b4d09c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -356,14 +356,17 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { mScrollXAfterMeasure = NO_SCROLL_POSITION; } - // Call with the present values in order to re-layout if necessary - // If a "pending" value has been set, we restore that value. - // That value gets cleared by reactScrollTo. - int scrollToX = - pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); - int scrollToY = - pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); - scrollTo(scrollToX, scrollToY); + // Apply pending contentOffset in case it was set before the view was laid out. + if (isContentReady()) { + // If a "pending" content offset value has been set, we restore that value. + // Upon call to scrollTo, the "pending" values will be re-set. + int scrollToX = + pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); + int scrollToY = + pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); + scrollTo(scrollToX, scrollToY); + } + ReactScrollViewHelper.emitLayoutEvent(this); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 7a619882dfdd14..e42e648f56a6f7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -286,14 +286,17 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { - // Call with the present values in order to re-layout if necessary - // If a "pending" content offset value has been set, we restore that value. - // Upon call to scrollTo, the "pending" values will be re-set. - int scrollToX = - pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); - int scrollToY = - pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); - scrollTo(scrollToX, scrollToY); + // Apply pending contentOffset in case it was set before the view was laid out. + if (isContentReady()) { + // If a "pending" content offset value has been set, we restore that value. + // Upon call to scrollTo, the "pending" values will be re-set. + int scrollToX = + pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX(); + int scrollToY = + pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); + scrollTo(scrollToX, scrollToY); + } + ReactScrollViewHelper.emitLayoutEvent(this); }