diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index cb16e9b6e507..736404a05933 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -438,6 +438,21 @@ var ScrollResponderMixin = { ); }, + /** + * A helper function to scroll by a specific offset in the ScrollView. + * This is useful when you want to change the size of the content view and scroll + * position at the same time. Syntax: + * + * scrollResponderScrollBy(options: {deltaX: number = 0; deltaY: number = 0; animated: boolean = true}) + */ + scrollResponderScrollBy: function(options : {deltaX?: number, deltaY?: number, animated?: boolean}) { + UIManager.dispatchViewManagerCommand( + this.scrollResponderGetScrollableNode(), + UIManager.RCTScrollView.Commands.scrollBy, + [options.deltaX || 0, options.deltaY || 0, options.animated !== false], + ); + }, + /** * Deprecated, do not use. */ diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 980d5d734e0e..5beb3bbddc20 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -530,6 +530,19 @@ const ScrollView = createReactClass({ }); }, + /** + * Scrolls by a given offset, either immediately or with a smooth animation. + * + * Syntax: + * + * `scrollBy(options: {deltaX: number = 0; deltaY: number = 0; animated: boolean = true})` + */ + scrollBy: function(options: { deltaX?: number, deltaY?: number, animated?: boolean } ) { + const data = {deltaX: options.deltaX || 0, deltaY: options.deltaY || 0, + animated: options.animated !== false}; + this.getScrollResponder().scrollResponderScrollBy(data); + }, + /** * Deprecated, use `scrollTo` instead. */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java index 9d4075791949..51ff4fc5cfc2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollViewManager.java @@ -150,6 +150,19 @@ public void scrollToEnd( } } + @Override + public void scrollBy( + ReactHorizontalScrollView scrollView, + ReactScrollViewCommandHelper.ScrollByCommandData data) { + int x = scrollView.getScrollX() + data.mDeltaX; + int y = scrollView.getScrollY() + data.mDeltaY; + if (data.mAnimated) { + scrollView.smoothScrollTo(x, y); + } else { + scrollView.scrollTo(x, y); + } + } + /** * When set, fills the rest of the scrollview with a color to avoid setting a background and * creating unnecessary overdraw. diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewCommandHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewCommandHelper.java index 0e2df5918c15..d9152bb3d267 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewCommandHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewCommandHelper.java @@ -25,11 +25,13 @@ public class ReactScrollViewCommandHelper { public static final int COMMAND_SCROLL_TO = 1; public static final int COMMAND_SCROLL_TO_END = 2; public static final int COMMAND_FLASH_SCROLL_INDICATORS = 3; + public static final int COMMAND_SCROLL_BY = 4; public interface ScrollCommandHandler { void scrollTo(T scrollView, ScrollToCommandData data); void scrollToEnd(T scrollView, ScrollToEndCommandData data); void flashScrollIndicators(T scrollView); + void scrollBy(T scrollView, ScrollByCommandData data); } public static class ScrollToCommandData { @@ -53,6 +55,18 @@ public static class ScrollToEndCommandData { } } + public static class ScrollByCommandData { + + public final int mDeltaX, mDeltaY; + public final boolean mAnimated; + + ScrollByCommandData(int deltaX, int deltaY, boolean animated) { + mDeltaX = deltaX; + mDeltaY = deltaY; + mAnimated = animated; + } + } + public static Map getCommandsMap() { return MapBuilder.of( "scrollTo", @@ -60,7 +74,9 @@ public static Map getCommandsMap() { "scrollToEnd", COMMAND_SCROLL_TO_END, "flashScrollIndicators", - COMMAND_FLASH_SCROLL_INDICATORS); + COMMAND_FLASH_SCROLL_INDICATORS, + "scrollBy", + COMMAND_SCROLL_BY); } public static void receiveCommand( @@ -88,6 +104,14 @@ public static void receiveCommand( viewManager.flashScrollIndicators(scrollView); return; + case COMMAND_SCROLL_BY: { + int deltaX = Math.round(PixelUtil.toPixelFromDIP(args.getDouble(0))); + int deltaY = Math.round(PixelUtil.toPixelFromDIP(args.getDouble(1))); + boolean animated = args.getBoolean(2); + + viewManager.scrollBy(scrollView, new ScrollByCommandData(deltaX, deltaY, animated)); + return; + } default: throw new IllegalArgumentException(String.format( "Unsupported command %d received by %s.", 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 a7d00cd52da6..2959cacd908f 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 @@ -210,6 +210,19 @@ public void scrollToEnd( } } + @Override + public void scrollBy( + ReactScrollView scrollView, + ReactScrollViewCommandHelper.ScrollByCommandData data) { + int x = scrollView.getScrollX() + data.mDeltaX; + int y = scrollView.getScrollY() + data.mDeltaY; + if (data.mAnimated) { + scrollView.smoothScrollTo(x, y); + } else { + scrollView.scrollTo(x, y); + } + } + @Override public @Nullable Map getExportedCustomDirectEventTypeConstants() { return createExportedCustomDirectEventTypeConstants();