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 e41ae5caa2d568..cff0dd58dd18ae 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 @@ -18,6 +18,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.widget.HorizontalScrollView; import android.widget.OverScroller; import androidx.annotation.Nullable; @@ -39,7 +40,8 @@ /** Similar to {@link ReactScrollView} but only supports horizontal scrolling. */ public class ReactHorizontalScrollView extends HorizontalScrollView - implements ReactClippingViewGroup { + implements ReactClippingViewGroup, ViewGroup.OnHierarchyChangeListener, + View.OnLayoutChangeListener { private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; @@ -68,6 +70,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private @Nullable List mSnapOffsets; private boolean mSnapToStart = true; private boolean mSnapToEnd = true; + private View mContentView; private ReactViewBackgroundManager mReactBackgroundManager; private boolean mPagedArrowScrolling = false; @@ -83,6 +86,7 @@ public ReactHorizontalScrollView(Context context, @Nullable FpsListener fpsListe mFpsListener = fpsListener; mScroller = getOverScrollerFromParent(); + setOnHierarchyChangeListener(this); } @Nullable @@ -537,6 +541,51 @@ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolea super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); } + private int getMaxScrollX() { + int contentWidth = mContentView.getWidth(); + int viewportWidth = getWidth() - getPaddingLeft() - getPaddingRight(); + return Math.max(0, contentWidth - viewportWidth); + } + + @Override + public void onChildViewAdded(View parent, View child) { + mContentView = child; + mContentView.addOnLayoutChangeListener(this); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + mContentView.removeOnLayoutChangeListener(this); + mContentView = null; + } + + /** + * Called when a mContentView's layout has changed. Fixes the scroll position if it's too large + * after the content resizes. Without this, the user would see a blank ScrollView when the scroll + * position is larger than the ScrollView's max scroll position after the content shrinks. + */ + @Override + public void onLayoutChange( + View v, + int left, + int top, + int right, + int bottom, + int oldLeft, + int oldTop, + int oldRight, + int oldBottom) { + if (mContentView == null) { + return; + } + + int currentScrollX = getScrollX(); + int maxScrollX = getMaxScrollX(); + if (currentScrollX > maxScrollX) { + scrollTo(maxScrollX, getScrollY()); + } + } + private void enableFpsListener() { if (isScrollPerfLoggingEnabled()) { Assertions.assertNotNull(mFpsListener);