mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-06-18 22:05:06 +08:00
Open source ViewPagerAndroid
Reviewed By: @foghina Differential Revision: D2513014 fb-gh-sync-id: d9bb668d76939ad85b657233c8b7beac9b244fab
This commit is contained in:
committed by
facebook-github-bot-4
parent
6244fd003d
commit
0a419650ce
@@ -32,6 +32,7 @@ var COMPONENTS = [
|
||||
require('./ToolbarAndroidExample'),
|
||||
require('./TouchableExample'),
|
||||
require('./ViewExample'),
|
||||
require('./ViewPagerAndroidExample.android'),
|
||||
];
|
||||
|
||||
var APIS = [
|
||||
|
||||
223
Examples/UIExplorer/ViewPagerAndroidExample.android.js
Normal file
223
Examples/UIExplorer/ViewPagerAndroidExample.android.js
Normal file
@@ -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 (
|
||||
<View style={styles.likeContainer}>
|
||||
<TouchableOpacity onPress={this.onClick} style={styles.likeButton}>
|
||||
<Text style={styles.likesText}>
|
||||
{thumbsUp + ' Like'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.likesText}>
|
||||
{this.state.likes + ' likes'}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var Button = React.createClass({
|
||||
_handlePress: function() {
|
||||
if (this.props.enabled && this.props.onPress) {
|
||||
this.props.onPress();
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._handlePress}>
|
||||
<View style={[styles.button, this.props.enabled ? {} : styles.buttonDisabled]}>
|
||||
<Text style={styles.buttonText}>{this.props.text}</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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 (
|
||||
<View style={[styles.progressBarContainer, {width: this.props.size}]}>
|
||||
<View style={[styles.progressBar, {width: progressBarSize}]}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ViewPagerAndroidExample = React.createClass({
|
||||
statics: {
|
||||
title: '<ViewPagerAndroid>',
|
||||
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(
|
||||
<View key={i} style={pageStyle} collapsable={false}>
|
||||
<Image
|
||||
style={styles.image}
|
||||
source={{uri: IMAGE_URIS[i % BGCOLOR.length]}}
|
||||
/>
|
||||
<LikeCount />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
var page = this.state.page;
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ViewPagerAndroid
|
||||
style={styles.viewPager}
|
||||
initialPage={0}
|
||||
onPageScroll={this.onPageScroll}
|
||||
onPageSelected={this.onPageSelected}
|
||||
ref={viewPager => { this.viewPager = viewPager; }}>
|
||||
{pages}
|
||||
</ViewPagerAndroid>
|
||||
<View style={styles.buttons}>
|
||||
<Button text="Start" enabled={page > 0} onPress={() => this.go(0)}/>
|
||||
<Button text="Prev" enabled={page > 0} onPress={() => this.move(-1)}/>
|
||||
<Text style={styles.buttonText}>Page {page + 1} / {PAGES}</Text>
|
||||
<ProgressBar size={100} progress={this.state.progress}/>
|
||||
<Button text="Next" enabled={page < PAGES - 1} onPress={() => this.move(1)}/>
|
||||
<Button text="Last" enabled={page < PAGES - 1} onPress={() => this.go(PAGES - 1)}/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
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;
|
||||
178
Libraries/Components/ViewPager/ViewPagerAndroid.android.js
Normal file
178
Libraries/Components/ViewPager/ViewPagerAndroid.android.js
Normal file
@@ -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 `<View>`s and not composite components.
|
||||
* You can set style properties like `padding` or `backgroundColor` for each
|
||||
* child.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* render: function() {
|
||||
* return (
|
||||
* <ViewPagerAndroid
|
||||
* style={styles.viewPager}
|
||||
* initialPage={0}>
|
||||
* <View style={styles.pageStyle}>
|
||||
* <Text>First page</Text>
|
||||
* </View>
|
||||
* <View style={styles.pageStyle}>
|
||||
* <Text>Second page</Text>
|
||||
* </View>
|
||||
* </ViewPagerAndroid>
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* 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 <View>. 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 (
|
||||
<NativeAndroidViewPager
|
||||
ref={VIEWPAGER_REF}
|
||||
style={this.props.style}
|
||||
selectedPage={this.state.selectedPage}
|
||||
onPageScroll={this._onPageScroll}
|
||||
onPageSelected={this._onPageSelected}
|
||||
children={this._childrenWithOverridenStyle()}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var NativeAndroidViewPager = createReactNativeComponentClass({
|
||||
validAttributes: {
|
||||
...ReactNativeViewAttributes.UIView,
|
||||
...ViewPagerValidAttributes,
|
||||
},
|
||||
uiViewClassName: 'AndroidViewPager',
|
||||
});
|
||||
|
||||
module.exports = ViewPagerAndroid;
|
||||
13
Libraries/Components/ViewPager/ViewPagerAndroid.ios.js
Normal file
13
Libraries/Components/ViewPager/ViewPagerAndroid.ios.js
Normal file
@@ -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');
|
||||
1
Libraries/react-native/react-native.js
vendored
1
Libraries/react-native/react-native.js
vendored
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PageScrollEvent> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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<PageSelectedEvent> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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<View> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<ReactViewPager> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user