diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.java index d16e58de1..9f8ede653 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/ReactSwipeRefreshLayout.java @@ -11,10 +11,11 @@ package com.facebook.react.views.swiperefresh; import android.support.v4.widget.SwipeRefreshLayout; import android.view.MotionEvent; +import android.view.ViewConfiguration; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.uimanager.events.NativeGestureUtil; import com.facebook.react.uimanager.PixelUtil; +import com.facebook.react.uimanager.events.NativeGestureUtil; /** * Basic extension of {@link SwipeRefreshLayout} with ReactNative-specific functionality. @@ -24,13 +25,15 @@ public class ReactSwipeRefreshLayout extends SwipeRefreshLayout { private static final float DEFAULT_CIRCLE_TARGET = 64; private boolean mDidLayout = false; - private boolean mRefreshing = false; private float mProgressViewOffset = 0; - + private int mTouchSlop; + private float mPrevTouchX; + private boolean mIntercepted; public ReactSwipeRefreshLayout(ReactContext reactContext) { super(reactContext); + mTouchSlop = ViewConfiguration.get(reactContext).getScaledTouchSlop(); } @Override @@ -71,12 +74,50 @@ public class ReactSwipeRefreshLayout extends SwipeRefreshLayout { } } + /** + * {@link SwipeRefreshLayout} overrides {@link ViewGroup#requestDisallowInterceptTouchEvent} and + * swallows it. This means that any component underneath SwipeRefreshLayout will now interact + * incorrectly with Views that are above SwipeRefreshLayout. We fix that by transmitting the call + * to this View's parents. + */ + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (getParent() != null) { + getParent().requestDisallowInterceptTouchEvent(disallowIntercept); + } + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (super.onInterceptTouchEvent(ev)) { + if (shouldInterceptTouchEvent(ev) && super.onInterceptTouchEvent(ev)) { NativeGestureUtil.notifyNativeGestureStarted(this, ev); return true; } return false; } + + /** + * {@link SwipeRefreshLayout} completely bypasses ViewGroup's "disallowIntercept" by overriding + * {@link ViewGroup#onInterceptTouchEvent} and never calling super.onInterceptTouchEvent(). + * This means that horizontal scrolls will always be intercepted, even though they shouldn't, so + * we have to check for that manually here. + */ + private boolean shouldInterceptTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mPrevTouchX = ev.getX(); + mIntercepted = false; + break; + + case MotionEvent.ACTION_MOVE: + final float eventX = ev.getX(); + final float xDiff = Math.abs(eventX - mPrevTouchX); + + if (mIntercepted || xDiff > mTouchSlop) { + mIntercepted = true; + return false; + } + } + return true; + } }