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 084a86d9635..0dee6f7c8e9 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 @@ -24,11 +24,15 @@ import androidx.core.text.TextUtilsCompat; import androidx.core.view.ViewCompat; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.ReactConstants; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.uimanager.MeasureSpecAssertions; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactClippingViewGroup; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; +import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.views.view.ReactViewBackgroundManager; @@ -43,6 +47,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; + private static final String CONTENT_OFFSET_LEFT = "contentOffsetLeft"; + private static final String CONTENT_OFFSET_TOP = "contentOffsetTop"; private final OnScrollDispatchHelper mOnScrollDispatchHelper = new OnScrollDispatchHelper(); private final @Nullable OverScroller mScroller; @@ -70,6 +76,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private boolean mSnapToEnd = true; private ReactViewBackgroundManager mReactBackgroundManager; private boolean mPagedArrowScrolling = false; + private @Nullable StateWrapper mStateWrapper; private final Rect mTempRect = new Rect(); @@ -217,7 +224,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView @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 - scrollTo(getScrollX(), getScrollY()); + reactScrollTo(getScrollX(), getScrollY()); } /** @@ -383,6 +390,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView mVelocityHelper.calculateVelocity(ev); int action = ev.getAction() & MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_UP && mDragging) { + updateStateOnScroll(getScrollX(), getScrollY()); + float velocityX = mVelocityHelper.getXVelocity(); float velocityY = mVelocityHelper.getYVelocity(); ReactScrollViewHelper.emitScrollEndDragEvent(this, velocityX, velocityY); @@ -605,6 +614,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView ViewCompat.postOnAnimationDelayed( ReactHorizontalScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY); } else { + updateStateOnScroll(getScrollX(), getScrollY()); + if (mPagingEnabled && !mSnappingToPage) { // Only if we have pagingEnabled and we have not snapped to the page do we // need to continue checking for the scroll. And we cause that scroll by asking for @@ -698,7 +709,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView targetOffset = currentPage * interval; if (targetOffset != currentOffset) { mActivelyScrolling = true; - smoothScrollTo((int) targetOffset, getScrollY()); + reactSmoothScrollTo((int) targetOffset, getScrollY()); } } @@ -834,7 +845,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView postInvalidateOnAnimation(); } else { - smoothScrollTo(targetOffset, getScrollY()); + reactSmoothScrollTo(targetOffset, getScrollY()); } } @@ -857,7 +868,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView page = 0; } - smoothScrollTo(page * width, getScrollY()); + reactSmoothScrollTo(page * width, getScrollY()); handlePostTouchScrolling(0, 0); } @@ -885,4 +896,45 @@ public class ReactHorizontalScrollView extends HorizontalScrollView public void setBorderStyle(@Nullable String style) { mReactBackgroundManager.setBorderStyle(style); } + + /** + * Calls `smoothScrollTo` and updates state. + * + *

`smoothScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between + * scroll view and state. Calling raw `smoothScrollTo` doesn't update state. + */ + public void reactSmoothScrollTo(int x, int y) { + smoothScrollTo(x, y); + updateStateOnScroll(x, y); + } + + /** + * Calls `reactScrollTo` and updates state. + * + *

`reactScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between + * scroll view and state. Calling raw `reactScrollTo` doesn't update state. + */ + public void reactScrollTo(int x, int y) { + scrollTo(x, y); + updateStateOnScroll(x, y); + } + + public void updateState(@Nullable StateWrapper stateWrapper) { + mStateWrapper = stateWrapper; + } + + /** + * Called on any stabilized onScroll change to propagate content offset value to a Shadow Node. + */ + private void updateStateOnScroll(int scrollX, int scrollY) { + if (mStateWrapper == null) { + return; + } + + WritableMap map = new WritableNativeMap(); + map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); + map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); + + mStateWrapper.updateState(map); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 7054a4e5492..b7712b3d653 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -16,7 +16,9 @@ import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactClippingViewGroupHelper; +import com.facebook.react.uimanager.ReactStylesDiffMap; import com.facebook.react.uimanager.Spacing; +import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.ViewProps; @@ -62,6 +64,15 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager