From bb5aede6e320084b1aee04f8b61089e5578fd4da Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Mon, 23 May 2016 05:42:37 -0700 Subject: [PATCH] Fix RefreshControl race condition Summary: Improved version of #7317. `setRefreshing` and `setProgressViewOffset` needs to be called after the view has been layed out. Instead of using `post` to do that we update the `refreshing` and `progressViewOffset` values in the first call to `onLayout`. I also noticed that `progressViewOffset` default value wasn't exactly the same as when not calling `setProgressViewOffset` at all. Tweaked the values to match android defaults. **Test plan (required)** Make sure the integration test passes, In UIExplorer: test RefreshControl with `refreshing = true` initially, test `progressViewOffset`. Closes https://github.com/facebook/react-native/pull/7683 Differential Revision: D3334426 fbshipit-source-id: ddd63a5e9a6afe2b8b7fe6a25e875a40f4e888c6 --- .../swiperefresh/ReactSwipeRefreshLayout.java | 47 +++++++++++++++++++ .../SwipeRefreshLayoutManager.java | 26 ++-------- 2 files changed, 50 insertions(+), 23 deletions(-) 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 f458c0ab0..d16e58de1 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 @@ -14,16 +14,63 @@ import android.view.MotionEvent; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.events.NativeGestureUtil; +import com.facebook.react.uimanager.PixelUtil; /** * Basic extension of {@link SwipeRefreshLayout} with ReactNative-specific functionality. */ 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; + + public ReactSwipeRefreshLayout(ReactContext reactContext) { super(reactContext); } + @Override + public void setRefreshing(boolean refreshing) { + mRefreshing = refreshing; + + // `setRefreshing` must be called after the initial layout otherwise it + // doesn't work when mounting the component with `refreshing = true`. + // Known Android issue: https://code.google.com/p/android/issues/detail?id=77712 + if (mDidLayout) { + super.setRefreshing(refreshing); + } + } + + public void setProgressViewOffset(float offset) { + mProgressViewOffset = offset; + + // The view must be measured before calling `getProgressCircleDiameter` so + // don't do it before the initial layout. + if (mDidLayout) { + int diameter = getProgressCircleDiameter(); + int start = Math.round(PixelUtil.toPixelFromDIP(offset)) - diameter; + int end = Math.round(PixelUtil.toPixelFromDIP(offset + DEFAULT_CIRCLE_TARGET) - diameter); + setProgressViewOffset(false, start, end); + } + } + + @Override + public void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (!mDidLayout) { + mDidLayout = true; + + // Update values that must be set after initial layout. + setProgressViewOffset(mProgressViewOffset); + setRefreshing(mRefreshing); + } + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (super.onInterceptTouchEvent(ev)) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java index 5db04a1a7..1ae2bacf5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/swiperefresh/SwipeRefreshLayoutManager.java @@ -20,7 +20,6 @@ import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.SystemClock; -import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewGroupManager; @@ -33,8 +32,6 @@ import com.facebook.react.uimanager.annotations.ReactProp; */ public class SwipeRefreshLayoutManager extends ViewGroupManager { - public static final float REFRESH_TRIGGER_DISTANCE = 48; - @Override protected ReactSwipeRefreshLayout createViewInstance(ThemedReactContext reactContext) { return new ReactSwipeRefreshLayout(reactContext); @@ -74,30 +71,13 @@ public class SwipeRefreshLayoutManager extends ViewGroupManager