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
This commit is contained in:
Samuel Susla
2020-03-04 14:11:06 -08:00
committed by Facebook Github Bot
parent d892bb889b
commit 4f908a569a
2 changed files with 71 additions and 8 deletions
@@ -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.
*
* <p>`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.
*
* <p>`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);
}
}
@@ -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<ReactHori
return new ReactHorizontalScrollView(context, mFpsListener);
}
@Override
public Object updateState(
ReactHorizontalScrollView view,
ReactStylesDiffMap props,
@Nullable StateWrapper stateWrapper) {
view.updateState(stateWrapper);
return null;
}
@ReactProp(name = "scrollEnabled", defaultBoolean = true)
public void setScrollEnabled(ReactHorizontalScrollView view, boolean value) {
view.setScrollEnabled(value);
@@ -179,9 +190,9 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
public void scrollTo(
ReactHorizontalScrollView scrollView, ReactScrollViewCommandHelper.ScrollToCommandData data) {
if (data.mAnimated) {
scrollView.smoothScrollTo(data.mDestX, data.mDestY);
scrollView.reactSmoothScrollTo(data.mDestX, data.mDestY);
} else {
scrollView.scrollTo(data.mDestX, data.mDestY);
scrollView.reactScrollTo(data.mDestX, data.mDestY);
}
}
@@ -192,9 +203,9 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
// ScrollView always has one child - the scrollable area
int right = scrollView.getChildAt(0).getWidth() + scrollView.getPaddingRight();
if (data.mAnimated) {
scrollView.smoothScrollTo(right, scrollView.getScrollY());
scrollView.reactSmoothScrollTo(right, scrollView.getScrollY());
} else {
scrollView.scrollTo(right, scrollView.getScrollY());
scrollView.reactScrollTo(right, scrollView.getScrollY());
}
}