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 ea49df4e1ccbeb..38371436b8834a 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 @@ -16,6 +16,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.support.v4.view.ViewCompat; import android.view.MotionEvent; import android.view.View; import android.widget.HorizontalScrollView; @@ -30,6 +31,7 @@ /** * Similar to {@link ReactScrollView} but only supports horizontal scrolling. */ +@TargetApi(16) public class ReactHorizontalScrollView extends HorizontalScrollView implements ReactClippingViewGroup { @@ -271,7 +273,6 @@ public void draw(Canvas canvas) { * don't get any events from Android about this lifecycle, we do all our detection by creating a * runnable that checks if we scrolled in the last frame and if so assumes we are still scrolling. */ - @TargetApi(16) private void handlePostTouchScrolling(int velocityX, int velocityY) { // If we aren't going to do anything (send events or snap to page), we can early out. if (!mSendMomentumEvents && !mPagingEnabled && !isScrollPerfLoggingEnabled()) { @@ -298,14 +299,18 @@ public void run() { if (mActivelyScrolling) { // We are still scrolling so we just post to check again a frame later mActivelyScrolling = false; - ReactHorizontalScrollView.this.postOnAnimationDelayed(this, ReactScrollViewHelper.MOMENTUM_DELAY); + ViewCompat.postOnAnimationDelayed(ReactHorizontalScrollView.this, + this, + ReactScrollViewHelper.MOMENTUM_DELAY); } else { 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 it mSnappingToPage = true; smoothScrollToPage(0); - ReactHorizontalScrollView.this.postOnAnimationDelayed(this, ReactScrollViewHelper.MOMENTUM_DELAY); + ViewCompat.postOnAnimationDelayed(ReactHorizontalScrollView.this, + this, + ReactScrollViewHelper.MOMENTUM_DELAY); } else { if (mSendMomentumEvents) { ReactScrollViewHelper.emitScrollMomentumEndEvent(ReactHorizontalScrollView.this); @@ -316,7 +321,9 @@ public void run() { } } }; - postOnAnimationDelayed(mPostTouchRunnable, ReactScrollViewHelper.MOMENTUM_DELAY); + ViewCompat.postOnAnimationDelayed(ReactHorizontalScrollView.this, + mPostTouchRunnable, + ReactScrollViewHelper.MOMENTUM_DELAY); } /** 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 cae6588f2a3be5..a9c7960cf14ae5 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 @@ -9,6 +9,7 @@ package com.facebook.react.views.scroll; +import android.annotation.TargetApi; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; @@ -39,13 +40,14 @@ *

ReactScrollView only supports vertical scrolling. For horizontal scrolling, * use {@link ReactHorizontalScrollView}. */ +@TargetApi(11) public class ReactScrollView extends ScrollView implements ReactClippingViewGroup, ViewGroup.OnHierarchyChangeListener, View.OnLayoutChangeListener { - private static Field sScrollerField; + private static @Nullable Field sScrollerField; private static boolean sTriedToGetScrollerField = false; private final OnScrollDispatchHelper mOnScrollDispatchHelper = new OnScrollDispatchHelper(); - private final OverScroller mScroller; + private final @Nullable OverScroller mScroller; private final VelocityHelper mVelocityHelper = new VelocityHelper(); private @Nullable Rect mClippingRect; @@ -71,6 +73,15 @@ public ReactScrollView(ReactContext context, @Nullable FpsListener fpsListener) mFpsListener = fpsListener; mReactBackgroundManager = new ReactViewBackgroundManager(this); + mScroller = getOverScrollerFromParent(); + setOnHierarchyChangeListener(this); + setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY); + } + + @Nullable + private OverScroller getOverScrollerFromParent() { + OverScroller scroller; + if (!sTriedToGetScrollerField) { sTriedToGetScrollerField = true; try { @@ -86,32 +97,31 @@ public ReactScrollView(ReactContext context, @Nullable FpsListener fpsListener) if (sScrollerField != null) { try { - Object scroller = sScrollerField.get(this); - if (scroller instanceof OverScroller) { - mScroller = (OverScroller) scroller; + Object scrollerValue = sScrollerField.get(this); + if (scrollerValue instanceof OverScroller) { + scroller = (OverScroller) scrollerValue; } else { Log.w( ReactConstants.TAG, "Failed to cast mScroller field in ScrollView (probably due to OEM changes to AOSP)! " + "This app will exhibit the bounce-back scrolling bug :("); - mScroller = null; + scroller = null; } } catch (IllegalAccessException e) { throw new RuntimeException("Failed to get mScroller from ScrollView!", e); } } else { - mScroller = null; + scroller = null; } - setOnHierarchyChangeListener(this); - setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY); + return scroller; } public void setSendMomentumEvents(boolean sendMomentumEvents) { mSendMomentumEvents = sendMomentumEvents; } - public void setScrollPerfTag(String scrollPerfTag) { + public void setScrollPerfTag(@Nullable String scrollPerfTag) { mScrollPerfTag = scrollPerfTag; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index a7d00cd52da60a..aaba2fb7261121 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -9,6 +9,7 @@ package com.facebook.react.views.scroll; +import android.annotation.TargetApi; import android.graphics.Color; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; @@ -31,6 +32,7 @@ *

Note that {@link ReactScrollView} and {@link ReactHorizontalScrollView} are exposed to JS * as a single ScrollView component, configured via the {@code horizontal} boolean property. */ +@TargetApi(11) @ReactModule(name = ReactScrollViewManager.REACT_CLASS) public class ReactScrollViewManager extends ViewGroupManager @@ -98,7 +100,7 @@ public void setSendMomentumEvents(ReactScrollView view, boolean sendMomentumEven * @param scrollPerfTag */ @ReactProp(name = "scrollPerfTag") - public void setScrollPerfTag(ReactScrollView view, String scrollPerfTag) { + public void setScrollPerfTag(ReactScrollView view, @Nullable String scrollPerfTag) { view.setScrollPerfTag(scrollPerfTag); } @@ -191,8 +193,8 @@ public void setBorderWidth(ReactScrollView view, int index, float width) { }, customType = "Color") public void setBorderColor(ReactScrollView view, int index, Integer color) { float rgbComponent = - color == null ? YogaConstants.UNDEFINED : (float) ((int)color & 0x00FFFFFF); - float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) ((int)color >>> 24); + color == null ? YogaConstants.UNDEFINED : (float) (color & 0x00FFFFFF); + float alphaComponent = color == null ? YogaConstants.UNDEFINED : (float) (color >>> 24); view.setBorderColor(SPACING_TYPES[index], rgbComponent, alphaComponent); } @@ -211,12 +213,12 @@ public void scrollToEnd( } @Override - public @Nullable Map getExportedCustomDirectEventTypeConstants() { + public @Nullable Map getExportedCustomDirectEventTypeConstants() { return createExportedCustomDirectEventTypeConstants(); } - public static Map createExportedCustomDirectEventTypeConstants() { - return MapBuilder.builder() + public static Map createExportedCustomDirectEventTypeConstants() { + return MapBuilder.builder() .put(ScrollEventType.SCROLL.getJSEventName(), MapBuilder.of("registrationName", "onScroll")) .put(ScrollEventType.BEGIN_DRAG.getJSEventName(), MapBuilder.of("registrationName", "onScrollBeginDrag")) .put(ScrollEventType.END_DRAG.getJSEventName(), MapBuilder.of("registrationName", "onScrollEndDrag"))