From 1a33400d4755a7f6b230e8f22f6d28cb3ddcd717 Mon Sep 17 00:00:00 2001 From: Brent Vatne Date: Fri, 13 Nov 2015 08:03:54 -0800 Subject: [PATCH] Add setPageWithoutAnimation Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager. cc ide kmagiera Closes https://github.com/facebook/react-native/pull/3621 Reviewed By: svcscm Differential Revision: D2652056 Pulled By: mkonicek fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c --- ReactViewPager.java | 12 +++---- ReactViewPagerManager.java | 47 +++++++++++++++++++++---- ViewPagerAndroid.android.js | 68 ++++++++++++++++++++++--------------- 3 files changed, 87 insertions(+), 40 deletions(-) diff --git a/ReactViewPager.java b/ReactViewPager.java index 43944c2b..e2a4b0fc 100644 --- a/ReactViewPager.java +++ b/ReactViewPager.java @@ -133,6 +133,12 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } + public void setCurrentItemFromJs(int item, boolean animated) { + mIsCurrentItemFromJs = true; + setCurrentItem(item, animated); + mIsCurrentItemFromJs = false; + } + /*package*/ void addViewToAdapter(View child, int index) { getAdapter().addView(child, index); } @@ -148,10 +154,4 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { /*package*/ View getViewFromAdapter(int index) { return getAdapter().getViewAt(index); } - - /*package*/ void setCurrentItemFromJs(int item) { - mIsCurrentItemFromJs = true; - setCurrentItem(item); - mIsCurrentItemFromJs = false; - } } diff --git a/ReactViewPagerManager.java b/ReactViewPagerManager.java index 0002a787..10b131d9 100644 --- a/ReactViewPagerManager.java +++ b/ReactViewPagerManager.java @@ -13,11 +13,14 @@ import android.view.View; +import com.facebook.infer.annotation.Assertions; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; -import com.facebook.react.uimanager.ReactProp; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ViewGroupManager; +import javax.annotation.Nullable; + /** * Instance of {@link ViewManager} that provides native {@link ViewPager} view. */ @@ -25,6 +28,9 @@ public class ReactViewPagerManager extends ViewGroupManager { private static final String REACT_CLASS = "AndroidViewPager"; + public static final int COMMAND_SET_PAGE = 1; + public static final int COMMAND_SET_PAGE_WITHOUT_ANIMATION = 2; + @Override public String getName() { return REACT_CLASS; @@ -35,12 +41,6 @@ protected ReactViewPager createViewInstance(ThemedReactContext reactContext) { return new ReactViewPager(reactContext); } - @ReactProp(name = "selectedPage") - public void setSelectedPage(ReactViewPager view, int page) { - // TODO(8496821): Handle selectedPage property cleanup correctly, now defaults to 0 - view.setCurrentItemFromJs(page); - } - @Override public boolean needsCustomLayoutForChildren() { return true; @@ -54,6 +54,39 @@ public Map getExportedCustomDirectEventTypeConstants() { ); } + @Override + public Map getCommandsMap() { + return MapBuilder.of( + "setPage", + COMMAND_SET_PAGE, + "setPageWithoutAnimation", + COMMAND_SET_PAGE_WITHOUT_ANIMATION); + } + + @Override + public void receiveCommand( + ReactViewPager viewPager, + int commandType, + @Nullable ReadableArray args) { + Assertions.assertNotNull(viewPager); + Assertions.assertNotNull(args); + switch (commandType) { + case COMMAND_SET_PAGE: { + viewPager.setCurrentItemFromJs(args.getInt(0), true); + return; + } + case COMMAND_SET_PAGE_WITHOUT_ANIMATION: { + viewPager.setCurrentItemFromJs(args.getInt(0), false); + return; + } + default: + throw new IllegalArgumentException(String.format( + "Unsupported command %d received by %s.", + commandType, + getClass().getSimpleName())); + } + } + @Override public void addView(ReactViewPager parent, View child, int index) { parent.addViewToAdapter(child, index); diff --git a/ViewPagerAndroid.android.js b/ViewPagerAndroid.android.js index 45b10913..abf2514f 100644 --- a/ViewPagerAndroid.android.js +++ b/ViewPagerAndroid.android.js @@ -2,24 +2,24 @@ * Copyright 2004-present Facebook. All Rights Reserved. * * @providesModule ViewPagerAndroid + * @flow */ 'use strict'; +var NativeModules = require('NativeModules'); var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var ReactElement = require('ReactElement'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var ReactPropTypes = require('ReactPropTypes'); +var RCTUIManager = NativeModules.UIManager; + var createReactNativeComponentClass = require('createReactNativeComponentClass'); var dismissKeyboard = require('dismissKeyboard'); var VIEWPAGER_REF = 'viewPager'; -var ViewPagerValidAttributes = { - selectedPage: true, -}; - /** * Container that allows to flip left and right between child views. Each * child view of the `ViewPagerAndroid` will be treated as a separate page @@ -97,17 +97,17 @@ var ViewPagerAndroid = React.createClass({ ]), }, - getInitialState: function() { - return { - selectedPage: this.props.initialPage, - }; + componentDidMount: function() { + if (this.props.initialPage) { + this.setPageWithoutAnimation(this.props.initialPage); + } }, - getInnerViewNode: function() { + getInnerViewNode: function(): ReactComponent { return this.refs[VIEWPAGER_REF].getInnerViewNode(); }, - _childrenWithOverridenStyle: function() { + _childrenWithOverridenStyle: function(): Array { // Override styles so that each page will fill the parent. Native component // will handle positioning of elements, so it's not important to offset // them correctly. @@ -134,34 +134,51 @@ var ViewPagerAndroid = React.createClass({ return ReactElement.createElement(child.type, newProps); }); }, - _onPageScroll: function(event) { + + _onPageScroll: function(e: Event) { if (this.props.onPageScroll) { - this.props.onPageScroll(event); + this.props.onPageScroll(e); } if (this.props.keyboardDismissMode === 'on-drag') { dismissKeyboard(); } }, - _onPageSelected: function(event) { - var selectedPage = event.nativeEvent.position; - this.setState({ - selectedPage, - }); + + _onPageSelected: function(e: Event) { if (this.props.onPageSelected) { - this.props.onPageSelected(event); + this.props.onPageSelected(e); } }, - setPage: function(selectedPage) { - this.setState({ - selectedPage, - }); + + /** + * A helper function to scroll to a specific page in the ViewPager. + * The transition between pages will be animated. + */ + setPage: function(selectedPage: number) { + RCTUIManager.dispatchViewManagerCommand( + React.findNodeHandle(this), + RCTUIManager.AndroidViewPager.Commands.setPage, + [selectedPage], + ); }, + + /** + * A helper function to scroll to a specific page in the ViewPager. + * The transition between pages will be *not* be animated. + */ + setPageWithoutAnimation: function(selectedPage: number) { + RCTUIManager.dispatchViewManagerCommand( + React.findNodeHandle(this), + RCTUIManager.AndroidViewPager.Commands.setPageWithoutAnimation, + [selectedPage], + ); + }, + render: function() { return (