Skip to content

Commit 4f908a5

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Fix HorizontalScrollView not reporting its contentOffset
Summary: # Problem HorizontalScrollView was not reporting its `contentOffset` to Fabric, therefore `measure` was returning wrong value. # Solution Copy over implementation of `updateState` from scrollView. I noticed that there is a lot of duplication between the two classes, that's why I decided to duplicate `updateState` as well. Changelog: [Internal] Reviewed By: JoshuaGross, mdvacca Differential Revision: D20247552 fbshipit-source-id: 48b1eb0c11198a32531effe55301f8d554e92130
1 parent d892bb8 commit 4f908a5

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@
2424
import androidx.core.view.ViewCompat;
2525
import com.facebook.common.logging.FLog;
2626
import com.facebook.infer.annotation.Assertions;
27+
import com.facebook.react.bridge.WritableMap;
28+
import com.facebook.react.bridge.WritableNativeMap;
2729
import com.facebook.react.common.ReactConstants;
2830
import com.facebook.react.config.ReactFeatureFlags;
2931
import com.facebook.react.uimanager.MeasureSpecAssertions;
32+
import com.facebook.react.uimanager.PixelUtil;
3033
import com.facebook.react.uimanager.ReactClippingViewGroup;
3134
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
35+
import com.facebook.react.uimanager.StateWrapper;
3236
import com.facebook.react.uimanager.ViewProps;
3337
import com.facebook.react.uimanager.events.NativeGestureUtil;
3438
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -43,6 +47,8 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
4347

4448
private static @Nullable Field sScrollerField;
4549
private static boolean sTriedToGetScrollerField = false;
50+
private static final String CONTENT_OFFSET_LEFT = "contentOffsetLeft";
51+
private static final String CONTENT_OFFSET_TOP = "contentOffsetTop";
4652

4753
private final OnScrollDispatchHelper mOnScrollDispatchHelper = new OnScrollDispatchHelper();
4854
private final @Nullable OverScroller mScroller;
@@ -70,6 +76,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView
7076
private boolean mSnapToEnd = true;
7177
private ReactViewBackgroundManager mReactBackgroundManager;
7278
private boolean mPagedArrowScrolling = false;
79+
private @Nullable StateWrapper mStateWrapper;
7380

7481
private final Rect mTempRect = new Rect();
7582

@@ -217,7 +224,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
217224
@Override
218225
protected void onLayout(boolean changed, int l, int t, int r, int b) {
219226
// Call with the present values in order to re-layout if necessary
220-
scrollTo(getScrollX(), getScrollY());
227+
reactScrollTo(getScrollX(), getScrollY());
221228
}
222229

223230
/**
@@ -383,6 +390,8 @@ public boolean onTouchEvent(MotionEvent ev) {
383390
mVelocityHelper.calculateVelocity(ev);
384391
int action = ev.getAction() & MotionEvent.ACTION_MASK;
385392
if (action == MotionEvent.ACTION_UP && mDragging) {
393+
updateStateOnScroll(getScrollX(), getScrollY());
394+
386395
float velocityX = mVelocityHelper.getXVelocity();
387396
float velocityY = mVelocityHelper.getYVelocity();
388397
ReactScrollViewHelper.emitScrollEndDragEvent(this, velocityX, velocityY);
@@ -605,6 +614,8 @@ public void run() {
605614
ViewCompat.postOnAnimationDelayed(
606615
ReactHorizontalScrollView.this, this, ReactScrollViewHelper.MOMENTUM_DELAY);
607616
} else {
617+
updateStateOnScroll(getScrollX(), getScrollY());
618+
608619
if (mPagingEnabled && !mSnappingToPage) {
609620
// Only if we have pagingEnabled and we have not snapped to the page do we
610621
// need to continue checking for the scroll. And we cause that scroll by asking for
@@ -698,7 +709,7 @@ private void smoothScrollAndSnap(int velocity) {
698709
targetOffset = currentPage * interval;
699710
if (targetOffset != currentOffset) {
700711
mActivelyScrolling = true;
701-
smoothScrollTo((int) targetOffset, getScrollY());
712+
reactSmoothScrollTo((int) targetOffset, getScrollY());
702713
}
703714
}
704715

@@ -834,7 +845,7 @@ private void flingAndSnap(int velocityX) {
834845

835846
postInvalidateOnAnimation();
836847
} else {
837-
smoothScrollTo(targetOffset, getScrollY());
848+
reactSmoothScrollTo(targetOffset, getScrollY());
838849
}
839850
}
840851

@@ -857,7 +868,7 @@ private void smoothScrollToNextPage(int direction) {
857868
page = 0;
858869
}
859870

860-
smoothScrollTo(page * width, getScrollY());
871+
reactSmoothScrollTo(page * width, getScrollY());
861872
handlePostTouchScrolling(0, 0);
862873
}
863874

@@ -885,4 +896,45 @@ public void setBorderRadius(float borderRadius, int position) {
885896
public void setBorderStyle(@Nullable String style) {
886897
mReactBackgroundManager.setBorderStyle(style);
887898
}
899+
900+
/**
901+
* Calls `smoothScrollTo` and updates state.
902+
*
903+
* <p>`smoothScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between
904+
* scroll view and state. Calling raw `smoothScrollTo` doesn't update state.
905+
*/
906+
public void reactSmoothScrollTo(int x, int y) {
907+
smoothScrollTo(x, y);
908+
updateStateOnScroll(x, y);
909+
}
910+
911+
/**
912+
* Calls `reactScrollTo` and updates state.
913+
*
914+
* <p>`reactScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between
915+
* scroll view and state. Calling raw `reactScrollTo` doesn't update state.
916+
*/
917+
public void reactScrollTo(int x, int y) {
918+
scrollTo(x, y);
919+
updateStateOnScroll(x, y);
920+
}
921+
922+
public void updateState(@Nullable StateWrapper stateWrapper) {
923+
mStateWrapper = stateWrapper;
924+
}
925+
926+
/**
927+
* Called on any stabilized onScroll change to propagate content offset value to a Shadow Node.
928+
*/
929+
private void updateStateOnScroll(int scrollX, int scrollY) {
930+
if (mStateWrapper == null) {
931+
return;
932+
}
933+
934+
WritableMap map = new WritableNativeMap();
935+
map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX));
936+
map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY));
937+
938+
mStateWrapper.updateState(map);
939+
}
888940
}

ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import com.facebook.react.uimanager.DisplayMetricsHolder;
1717
import com.facebook.react.uimanager.PixelUtil;
1818
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
19+
import com.facebook.react.uimanager.ReactStylesDiffMap;
1920
import com.facebook.react.uimanager.Spacing;
21+
import com.facebook.react.uimanager.StateWrapper;
2022
import com.facebook.react.uimanager.ThemedReactContext;
2123
import com.facebook.react.uimanager.ViewGroupManager;
2224
import com.facebook.react.uimanager.ViewProps;
@@ -62,6 +64,15 @@ public ReactHorizontalScrollView createViewInstance(ThemedReactContext context)
6264
return new ReactHorizontalScrollView(context, mFpsListener);
6365
}
6466

67+
@Override
68+
public Object updateState(
69+
ReactHorizontalScrollView view,
70+
ReactStylesDiffMap props,
71+
@Nullable StateWrapper stateWrapper) {
72+
view.updateState(stateWrapper);
73+
return null;
74+
}
75+
6576
@ReactProp(name = "scrollEnabled", defaultBoolean = true)
6677
public void setScrollEnabled(ReactHorizontalScrollView view, boolean value) {
6778
view.setScrollEnabled(value);
@@ -179,9 +190,9 @@ public void flashScrollIndicators(ReactHorizontalScrollView scrollView) {
179190
public void scrollTo(
180191
ReactHorizontalScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) {
181192
if (data.mAnimated) {
182-
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
193+
scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY);
183194
} else {
184-
scrollView.scrollTo(data.mDestX, data.mDestY);
195+
scrollView.reactScrollTo(data.mDestX, data.mDestY);
185196
}
186197
}
187198

@@ -192,9 +203,9 @@ public void scrollToEnd(
192203
// ScrollView always has one child - the scrollable area
193204
int right = scrollView.getChildAt(0).getWidth() + scrollView.getPaddingRight();
194205
if (data.mAnimated) {
195-
scrollView.smoothScrollTo(right, scrollView.getScrollY());
206+
scrollView.reactSmoothScrollTo(right, scrollView.getScrollY());
196207
} else {
197-
scrollView.scrollTo(right, scrollView.getScrollY());
208+
scrollView.reactScrollTo(right, scrollView.getScrollY());
198209
}
199210
}
200211

0 commit comments

Comments
 (0)