diff --git a/Examples/UIExplorer/UIExplorerList.android.js b/Examples/UIExplorer/UIExplorerList.android.js
index d102d3de8..d04c80c6e 100644
--- a/Examples/UIExplorer/UIExplorerList.android.js
+++ b/Examples/UIExplorer/UIExplorerList.android.js
@@ -32,6 +32,7 @@ var COMPONENTS = [
require('./ToolbarAndroidExample'),
require('./TouchableExample'),
require('./ViewExample'),
+ require('./ViewPagerAndroidExample.android'),
];
var APIS = [
diff --git a/Examples/UIExplorer/ViewPagerAndroidExample.android.js b/Examples/UIExplorer/ViewPagerAndroidExample.android.js
new file mode 100644
index 000000000..8dcbbb06e
--- /dev/null
+++ b/Examples/UIExplorer/ViewPagerAndroidExample.android.js
@@ -0,0 +1,223 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ Image,
+ StyleSheet,
+ Text,
+ TouchableWithoutFeedback,
+ TouchableOpacity,
+ View,
+ ViewPagerAndroid,
+} = React;
+
+var PAGES = 5;
+var BGCOLOR = ['#fdc08e', '#fff6b9', '#99d1b7', '#dde5fe', '#f79273'];
+var IMAGE_URIS = [
+ 'http://apod.nasa.gov/apod/image/1410/20141008tleBaldridge001h990.jpg',
+ 'http://apod.nasa.gov/apod/image/1409/volcanicpillar_vetter_960.jpg',
+ 'http://apod.nasa.gov/apod/image/1409/m27_snyder_960.jpg',
+ 'http://apod.nasa.gov/apod/image/1409/PupAmulti_rot0.jpg',
+ 'http://apod.nasa.gov/apod/image/1510/lunareclipse_27Sep_beletskycrop4.jpg',
+];
+
+var LikeCount = React.createClass({
+ getInitialState: function() {
+ return {
+ likes: 7,
+ };
+ },
+ onClick: function() {
+ this.setState({likes: this.state.likes + 1});
+ },
+ render: function() {
+ var thumbsUp = '\uD83D\uDC4D';
+ return (
+
+
+
+ {thumbsUp + ' Like'}
+
+
+
+ {this.state.likes + ' likes'}
+
+
+ );
+ },
+});
+
+var Button = React.createClass({
+ _handlePress: function() {
+ if (this.props.enabled && this.props.onPress) {
+ this.props.onPress();
+ }
+ },
+ render: function() {
+ return (
+
+
+ {this.props.text}
+
+
+ );
+ }
+});
+
+var ProgressBar = React.createClass({
+ render: function() {
+ var fractionalPosition = (this.props.progress.position + this.props.progress.offset);
+ var progressBarSize = (fractionalPosition / (PAGES - 1)) * this.props.size;
+ return (
+
+
+
+ );
+ }
+});
+
+var ViewPagerAndroidExample = React.createClass({
+ statics: {
+ title: '',
+ description: 'Container that allows to flip left and right between child views.'
+ },
+ getInitialState: function() {
+ return {page: 0, progress: {position: 0, offset: 0}};
+ },
+ onPageSelected: function(e) {
+ this.setState({page: e.nativeEvent.position});
+ },
+ onPageScroll: function(e) {
+ this.setState({progress: e.nativeEvent});
+ },
+ move: function(delta) {
+ var page = this.state.page + delta;
+ this.viewPager && this.viewPager.setPage(page);
+ this.setState({page});
+ },
+ go: function(page) {
+ this.viewPager && this.viewPager.setPage(page);
+ this.setState({page});
+ },
+ render: function() {
+ var pages = [];
+ for (var i = 0; i < PAGES; i++) {
+ var pageStyle = {
+ backgroundColor: BGCOLOR[i % BGCOLOR.length],
+ alignItems: 'center',
+ padding: 20,
+ };
+ pages.push(
+
+
+
+
+ );
+ }
+ var page = this.state.page;
+ return (
+
+ { this.viewPager = viewPager; }}>
+ {pages}
+
+
+
+
+ );
+ },
+});
+
+var styles = StyleSheet.create({
+ buttons: {
+ flexDirection: 'row',
+ height: 30,
+ backgroundColor: 'black',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ },
+ button: {
+ flex: 1,
+ width: 0,
+ margin: 5,
+ borderColor: 'gray',
+ borderWidth: 1,
+ backgroundColor: 'gray',
+ },
+ buttonDisabled: {
+ backgroundColor: 'black',
+ opacity: 0.5,
+ },
+ buttonText: {
+ color: 'white',
+ },
+ container: {
+ flex: 1,
+ backgroundColor: 'white',
+ },
+ image: {
+ width: 300,
+ height: 200,
+ padding: 20,
+ },
+ likeButton: {
+ backgroundColor: 'rgba(0, 0, 0, 0.1)',
+ borderColor: '#333333',
+ borderWidth: 1,
+ borderRadius: 5,
+ flex: 1,
+ margin: 8,
+ padding: 8,
+ },
+ likeContainer: {
+ flexDirection: 'row',
+ },
+ likesText: {
+ flex: 1,
+ fontSize: 18,
+ alignSelf: 'center',
+ },
+ progressBarContainer: {
+ height: 10,
+ margin: 10,
+ borderColor: '#eeeeee',
+ borderWidth: 2,
+ },
+ progressBar: {
+ alignSelf: 'flex-start',
+ flex: 1,
+ backgroundColor: '#eeeeee',
+ },
+ viewPager: {
+ flex: 1,
+ },
+});
+
+module.exports = ViewPagerAndroidExample;
diff --git a/Libraries/Components/ViewPager/ViewPagerAndroid.android.js b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js
new file mode 100644
index 000000000..36c60820c
--- /dev/null
+++ b/Libraries/Components/ViewPager/ViewPagerAndroid.android.js
@@ -0,0 +1,178 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ *
+ * @providesModule ViewPagerAndroid
+ */
+'use strict';
+
+var NativeMethodsMixin = require('NativeMethodsMixin');
+var React = require('React');
+var ReactElement = require('ReactElement');
+var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
+var ReactPropTypes = require('ReactPropTypes');
+
+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
+ * and will be streched to fill the `ViewPagerAndroid`.
+ *
+ * It is important all children are ``s and not composite components.
+ * You can set style properties like `padding` or `backgroundColor` for each
+ * child.
+ *
+ * Example:
+ *
+ * ```
+ * render: function() {
+ * return (
+ *
+ *
+ * First page
+ *
+ *
+ * Second page
+ *
+ *
+ * );
+ * }
+ *
+ * ...
+ *
+ * var styles = {
+ * ...
+ * pageStyle: {
+ * alignItems: 'center',
+ * padding: 20,
+ * }
+ * }
+ * ```
+ */
+var ViewPagerAndroid = React.createClass({
+
+ propTypes: {
+ /**
+ * Index of initial page that should be selected. Use `setPage` method to
+ * update the page, and `onPageSelected` to monitor page changes
+ */
+ initialPage: ReactPropTypes.number,
+
+ /**
+ * Executed when transitioning between pages (ether because of animation for
+ * the requested page change or when user is swiping/dragging between pages)
+ * The `event.nativeEvent` object for this callback will carry following data:
+ * - position - index of first page from the left that is currently visible
+ * - offset - value from range [0,1) describing stage between page transitions.
+ * Value x means that (1 - x) fraction of the page at "position" index is
+ * visible, and x fraction of the next page is visible.
+ */
+ onPageScroll: ReactPropTypes.func,
+
+ /**
+ * This callback will be caleld once ViewPager finish navigating to selected page
+ * (when user swipes between pages). The `event.nativeEvent` object passed to this
+ * callback will have following fields:
+ * - position - index of page that has been selected
+ */
+ onPageSelected: ReactPropTypes.func,
+
+ /**
+ * Determines whether the keyboard gets dismissed in response to a drag.
+ * - 'none' (the default), drags do not dismiss the keyboard.
+ * - 'on-drag', the keyboard is dismissed when a drag begins.
+ */
+ keyboardDismissMode: ReactPropTypes.oneOf([
+ 'none', // default
+ 'on-drag',
+ ]),
+ },
+
+ getInitialState: function() {
+ return {
+ selectedPage: this.props.initialPage,
+ };
+ },
+
+ getInnerViewNode: function() {
+ return this.refs[VIEWPAGER_REF].getInnerViewNode();
+ },
+
+ _childrenWithOverridenStyle: function() {
+ // 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.
+ return React.Children.map(this.props.children, function(child) {
+ var newProps = {
+ ...child.props,
+ style: [child.props.style, {
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ right: 0,
+ bottom: 0,
+ width: undefined,
+ height: undefined,
+ }],
+ collapsable: false,
+ };
+ if (child.type && child.type.displayName && (child.type.displayName !== 'View')) {
+ console.warn('Each ViewPager child must be a . Was ' + child.type.displayName);
+ }
+ return ReactElement.createElement(child.type, newProps);
+ });
+ },
+ _onPageScroll: function(event) {
+ if (this.props.onPageScroll) {
+ this.props.onPageScroll(event);
+ }
+ if (this.props.keyboardDismissMode === 'on-drag') {
+ dismissKeyboard();
+ }
+ },
+ _onPageSelected: function(event) {
+ var selectedPage = event.nativeEvent.position;
+ this.setState({
+ selectedPage,
+ });
+ if (this.props.onPageSelected) {
+ this.props.onPageSelected(event);
+ }
+ },
+ setPage: function(selectedPage) {
+ this.setState({
+ selectedPage,
+ });
+ },
+ render: function() {
+ return (
+
+ );
+ },
+});
+
+var NativeAndroidViewPager = createReactNativeComponentClass({
+ validAttributes: {
+ ...ReactNativeViewAttributes.UIView,
+ ...ViewPagerValidAttributes,
+ },
+ uiViewClassName: 'AndroidViewPager',
+});
+
+module.exports = ViewPagerAndroid;
diff --git a/Libraries/Components/ViewPager/ViewPagerAndroid.ios.js b/Libraries/Components/ViewPager/ViewPagerAndroid.ios.js
new file mode 100644
index 000000000..35d4196b4
--- /dev/null
+++ b/Libraries/Components/ViewPager/ViewPagerAndroid.ios.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ViewPagerAndroid
+ */
+'use strict';
+
+module.exports = require('UnimplementedView');
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index 2b418e3a0..51f3d16cf 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -48,6 +48,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
TouchableOpacity: require('TouchableOpacity'),
TouchableWithoutFeedback: require('TouchableWithoutFeedback'),
View: require('View'),
+ ViewPagerAndroid: require('ViewPagerAndroid'),
WebView: require('WebView'),
// APIs
diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
index 65b7b38bc..f4b011d46 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
@@ -34,6 +34,7 @@ import com.facebook.react.views.text.ReactVirtualTextViewManager;
import com.facebook.react.views.textinput.ReactTextInputManager;
import com.facebook.react.views.toolbar.ReactToolbarManager;
import com.facebook.react.views.view.ReactViewManager;
+import com.facebook.react.views.viewpager.ReactViewPagerManager;
/**
* Package defining basic modules and view managers.
@@ -68,6 +69,7 @@ public class MainReactPackage implements ReactPackage {
new ReactTextViewManager(),
new ReactToolbarManager(),
new ReactViewManager(),
+ new ReactViewPagerManager(),
new ReactVirtualTextViewManager());
}
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java
new file mode 100644
index 000000000..2bd0503bf
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageScrollEvent.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.viewpager;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.uimanager.events.Event;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+
+/**
+ * Event emitted by {@link ReactViewPager} when user scrolls between pages (or when animating
+ * between pages).
+ *
+ * Additional data provided by this event:
+ * - position - index of first page from the left that is currently visible
+ * - offset - value from range [0,1) describing stage between page transitions. Value x means that
+ * (1 - x) fraction of the page at "position" index is visible, and x fraction of the next page
+ * is visible.
+ */
+/* package */ class PageScrollEvent extends Event {
+
+ public static final String EVENT_NAME = "topPageScroll";
+
+ private final int mPosition;
+ private final float mOffset;
+
+ protected PageScrollEvent(int viewTag, long timestampMs, int position, float offset) {
+ super(viewTag, timestampMs);
+ mPosition = position;
+ mOffset = offset;
+ }
+
+ @Override
+ public String getEventName() {
+ return EVENT_NAME;
+ }
+
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
+ }
+
+ private WritableMap serializeEventData() {
+ WritableMap eventData = Arguments.createMap();
+ eventData.putInt("position", mPosition);
+ eventData.putDouble("offset", mOffset);
+ return eventData;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java
new file mode 100644
index 000000000..0b68dcbed
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/PageSelectedEvent.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.viewpager;
+
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.uimanager.events.Event;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+
+/**
+ * Event emitted by {@link ReactViewPager} when selected page changes.
+ *
+ * Additional data provided by this event:
+ * - position - index of page that has been selected
+ */
+/* package */ class PageSelectedEvent extends Event {
+
+ public static final String EVENT_NAME = "topPageSelected";
+
+ private final int mPosition;
+
+ protected PageSelectedEvent(int viewTag, long timestampMs, int position) {
+ super(viewTag, timestampMs);
+ mPosition = position;
+ }
+
+ @Override
+ public String getEventName() {
+ return EVENT_NAME;
+ }
+
+ @Override
+ public void dispatch(RCTEventEmitter rctEventEmitter) {
+ rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
+ }
+
+ private WritableMap serializeEventData() {
+ WritableMap eventData = Arguments.createMap();
+ eventData.putInt("position", mPosition);
+ return eventData;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java
new file mode 100644
index 000000000..ad669a7c6
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.viewpager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.os.SystemClock;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.events.EventDispatcher;
+import com.facebook.react.uimanager.events.NativeGestureUtil;
+
+/**
+ * Wrapper view for {@link ViewPager}. It's forwarding calls to {@link ViewGroup#addView} to add
+ * views to custom {@link PagerAdapter} instance which is used by {@link NativeViewHierarchyManager}
+ * to add children nodes according to react views hierarchy.
+ */
+/* package */ class ReactViewPager extends ViewPager {
+
+ private class Adapter extends PagerAdapter {
+
+ private List mViews = new ArrayList<>();
+
+ void addView(View child, int index) {
+ mViews.add(index, child);
+ notifyDataSetChanged();
+ // This will prevent view pager from detaching views for pages that are not currently selected
+ // We need to do that since {@link ViewPager} relies on layout passes to position those views
+ // in a right way (also thanks to {@link ReactViewPagerManager#needsCustomLayoutForChildren}
+ // returning {@code true}). Currently we only call {@link View#measure} and
+ // {@link View#layout} after CSSLayout step.
+
+ // TODO(7323049): Remove this workaround once we figure out a way to re-layout some views on
+ // request
+ setOffscreenPageLimit(mViews.size());
+ }
+
+ @Override
+ public int getCount() {
+ return mViews.size();
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ View view = mViews.get(position);
+ container.addView(view, 0, generateDefaultLayoutParams());
+ return view;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ View view = mViews.get(position);
+ container.removeView(view);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+ }
+
+ private class PageChangeListener implements OnPageChangeListener {
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mEventDispatcher.dispatchEvent(
+ new PageScrollEvent(getId(), SystemClock.uptimeMillis(), position, positionOffset));
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (!mIsCurrentItemFromJs) {
+ mEventDispatcher.dispatchEvent(
+ new PageSelectedEvent(getId(), SystemClock.uptimeMillis(), position));
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ // don't send events
+ }
+ }
+
+ private final EventDispatcher mEventDispatcher;
+ private boolean mIsCurrentItemFromJs;
+
+ public ReactViewPager(ReactContext reactContext) {
+ super(reactContext);
+ mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
+ mIsCurrentItemFromJs = false;
+ setOnPageChangeListener(new PageChangeListener());
+ setAdapter(new Adapter());
+ }
+
+ @Override
+ public Adapter getAdapter() {
+ return (Adapter) super.getAdapter();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (super.onInterceptTouchEvent(ev)) {
+ NativeGestureUtil.notifyNativeGestureStarted(this, ev);
+ return true;
+ }
+ return false;
+ }
+
+ /* package */ void addViewToAdapter(View child, int index) {
+ getAdapter().addView(child, index);
+ }
+
+ /* package */ void setCurrentItemFromJs(int item) {
+ mIsCurrentItemFromJs = true;
+ setCurrentItem(item);
+ mIsCurrentItemFromJs = false;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java
new file mode 100644
index 000000000..05fd1e76d
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.views.viewpager;
+
+import java.util.Map;
+
+import android.view.View;
+
+import com.facebook.react.common.MapBuilder;
+import com.facebook.react.uimanager.ReactProp;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.ViewGroupManager;
+
+/**
+ * Instance of {@link ViewManager} that provides native {@link ViewPager} view.
+ */
+public class ReactViewPagerManager extends ViewGroupManager {
+
+ private static final String REACT_CLASS = "AndroidViewPager";
+
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+
+ @Override
+ 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;
+ }
+
+ @Override
+ public Map getExportedCustomDirectEventTypeConstants() {
+ return MapBuilder.of(
+ PageScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScroll"),
+ PageSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageSelected")
+ );
+ }
+
+ @Override
+ public void addView(ReactViewPager parent, View child, int index) {
+ parent.addViewToAdapter(child, index);
+ }
+}