mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Updates from Fri 26, Jun
This commit is contained in:
@@ -23,6 +23,7 @@ var TypesEnum = {
|
||||
easeInEaseOut: true,
|
||||
easeIn: true,
|
||||
easeOut: true,
|
||||
keyboard: true,
|
||||
};
|
||||
var Types = keyMirror(TypesEnum);
|
||||
|
||||
@@ -113,4 +114,10 @@ var LayoutAnimation = {
|
||||
}
|
||||
};
|
||||
|
||||
for (var key in LayoutAnimation.Presets) {
|
||||
LayoutAnimation[key] = LayoutAnimation.configureNext.bind(
|
||||
null, LayoutAnimation.Presets[key]
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = LayoutAnimation;
|
||||
|
||||
@@ -35,6 +35,34 @@ type MapRegion = {
|
||||
var MapView = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
checkAnnotationIds: function (annotations: Array<Object>) {
|
||||
|
||||
var newAnnotations = annotations.map(function (annotation) {
|
||||
if (!annotation.id) {
|
||||
// TODO: add a base64 (or similar) encoder here
|
||||
annotation.id = encodeURIComponent(JSON.stringify(annotation));
|
||||
}
|
||||
|
||||
return annotation;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
annotations: newAnnotations
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
if (this.props.annotations) {
|
||||
this.checkAnnotationIds(this.props.annotations);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps: Object) {
|
||||
if (nextProps.annotations) {
|
||||
this.checkAnnotationIds(nextProps.annotations);
|
||||
}
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* Used to style and layout the `MapView`. See `StyleSheet.js` and
|
||||
@@ -84,14 +112,14 @@ var MapView = React.createClass({
|
||||
|
||||
/**
|
||||
* The map type to be displayed.
|
||||
*
|
||||
*
|
||||
* - standard: standard road map (default)
|
||||
* - satellite: satellite view
|
||||
* - hybrid: satellite view with roads and points of interest overlayed
|
||||
*/
|
||||
mapType: React.PropTypes.oneOf([
|
||||
'standard',
|
||||
'satellite',
|
||||
'standard',
|
||||
'satellite',
|
||||
'hybrid',
|
||||
]),
|
||||
|
||||
@@ -126,11 +154,34 @@ var MapView = React.createClass({
|
||||
latitude: React.PropTypes.number.isRequired,
|
||||
longitude: React.PropTypes.number.isRequired,
|
||||
|
||||
/**
|
||||
* Whether the pin drop should be animated or not
|
||||
*/
|
||||
animateDrop: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Annotation title/subtile.
|
||||
*/
|
||||
title: React.PropTypes.string,
|
||||
subtitle: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* Whether the Annotation has callout buttons.
|
||||
*/
|
||||
hasLeftCallout: React.PropTypes.bool,
|
||||
hasRightCallout: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Event handlers for callout buttons.
|
||||
*/
|
||||
onLeftCalloutPress: React.PropTypes.func,
|
||||
onRightCalloutPress: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* annotation id
|
||||
*/
|
||||
id: React.PropTypes.string
|
||||
|
||||
})),
|
||||
|
||||
/**
|
||||
@@ -158,6 +209,11 @@ var MapView = React.createClass({
|
||||
* Callback that is called once, when the user is done moving the map.
|
||||
*/
|
||||
onRegionChangeComplete: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback that is called once, when the user is clicked on a annotation.
|
||||
*/
|
||||
onAnnotationPress: React.PropTypes.func,
|
||||
},
|
||||
|
||||
_onChange: function(event: Event) {
|
||||
@@ -170,8 +226,34 @@ var MapView = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_onPress: function(event: Event) {
|
||||
if (event.nativeEvent.action === 'annotation-click') {
|
||||
this.props.onAnnotationPress && this.props.onAnnotationPress(event.nativeEvent.annotation);
|
||||
}
|
||||
|
||||
if (event.nativeEvent.action === 'callout-click') {
|
||||
if (!this.props.annotations) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the annotation with the id of what has been pressed
|
||||
for (var i = 0; i < this.props.annotations.length; i++) {
|
||||
var annotation = this.props.annotations[i];
|
||||
if (annotation.id === event.nativeEvent.annotationId) {
|
||||
// Pass the right function
|
||||
if (event.nativeEvent.side === 'left') {
|
||||
annotation.onLeftCalloutPress && annotation.onLeftCalloutPress(event.nativeEvent);
|
||||
} else if (event.nativeEvent.side === 'right') {
|
||||
annotation.onRightCalloutPress && annotation.onRightCalloutPress(event.nativeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <RCTMap {...this.props} onChange={this._onChange} />;
|
||||
return <RCTMap {...this.props} onPress={this._onPress} onChange={this._onChange} />;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -179,6 +261,7 @@ if (Platform.OS === 'android') {
|
||||
var RCTMap = createReactNativeComponentClass({
|
||||
validAttributes: merge(
|
||||
ReactNativeViewAttributes.UIView, {
|
||||
active: true,
|
||||
showsUserLocation: true,
|
||||
zoomEnabled: true,
|
||||
rotateEnabled: true,
|
||||
|
||||
@@ -57,6 +57,7 @@ var RCTNavigatorItem = createReactNativeComponentClass({
|
||||
backButtonIcon: true,
|
||||
backButtonTitle: true,
|
||||
tintColor: true,
|
||||
translucent: true,
|
||||
navigationBarHidden: true,
|
||||
titleTextColor: true,
|
||||
style: true,
|
||||
@@ -300,6 +301,11 @@ var NavigatorIOS = React.createClass({
|
||||
*/
|
||||
titleTextColor: PropTypes.string,
|
||||
|
||||
/**
|
||||
* A Boolean value that indicates whether the navigation bar is translucent
|
||||
*/
|
||||
translucent: PropTypes.bool,
|
||||
|
||||
},
|
||||
|
||||
navigator: (undefined: ?Object),
|
||||
@@ -609,6 +615,7 @@ var NavigatorIOS = React.createClass({
|
||||
navigationBarHidden={this.props.navigationBarHidden}
|
||||
tintColor={this.props.tintColor}
|
||||
barTintColor={this.props.barTintColor}
|
||||
translucent={this.props.translucent}
|
||||
titleTextColor={this.props.titleTextColor}>
|
||||
<Component
|
||||
navigator={this.navigator}
|
||||
|
||||
@@ -276,7 +276,9 @@ var ListView = React.createClass({
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
this._measureAndUpdateScrollProps();
|
||||
this.requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
});
|
||||
},
|
||||
|
||||
onRowHighlighted: function(sectionID, rowID) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
|
||||
var Dimensions = require('Dimensions');
|
||||
var InteractionMixin = require('InteractionMixin');
|
||||
var Map = require('Map');
|
||||
var NavigationContext = require('NavigationContext');
|
||||
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
|
||||
var NavigatorNavigationBar = require('NavigatorNavigationBar');
|
||||
@@ -257,6 +258,8 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
this._renderedSceneMap = new Map();
|
||||
|
||||
var routeStack = this.props.initialRouteStack || [this.props.initialRoute];
|
||||
invariant(
|
||||
routeStack.length >= 1,
|
||||
@@ -276,10 +279,6 @@ var Navigator = React.createClass({
|
||||
),
|
||||
idStack: routeStack.map(() => getuid()),
|
||||
routeStack,
|
||||
// `updatingRange*` allows us to only render the visible or staged scenes
|
||||
// On first render, we will render every scene in the initialRouteStack
|
||||
updatingRangeStart: 0,
|
||||
updatingRangeLength: routeStack.length,
|
||||
presentedIndex: initialRouteIndex,
|
||||
transitionFromIndex: null,
|
||||
activeGesture: null,
|
||||
@@ -351,8 +350,6 @@ var Navigator = React.createClass({
|
||||
sceneConfigStack: nextRouteStack.map(
|
||||
this.props.configureScene
|
||||
),
|
||||
updatingRangeStart: 0,
|
||||
updatingRangeLength: nextRouteStack.length,
|
||||
presentedIndex: destIndex,
|
||||
activeGesture: null,
|
||||
transitionFromIndex: null,
|
||||
@@ -395,7 +392,6 @@ var Navigator = React.createClass({
|
||||
this.spring.getSpringConfig().tension = sceneConfig.springTension;
|
||||
this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity);
|
||||
this.spring.setEndValue(1);
|
||||
this._emitWillFocus(this.state.routeStack[this.state.presentedIndex]);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -461,6 +457,7 @@ var Navigator = React.createClass({
|
||||
if (this.state.transitionQueue.length) {
|
||||
var queuedTransition = this.state.transitionQueue.shift();
|
||||
this._enableScene(queuedTransition.destIndex);
|
||||
this._emitWillFocus(this.state.routeStack[queuedTransition.destIndex]);
|
||||
this._transitionTo(
|
||||
queuedTransition.destIndex,
|
||||
queuedTransition.velocity,
|
||||
@@ -660,6 +657,7 @@ var Navigator = React.createClass({
|
||||
}
|
||||
} else {
|
||||
// The gesture has enough velocity to complete, so we transition to the gesture's destination
|
||||
this._emitWillFocus(this.state.routeStack[destIndex]);
|
||||
this._transitionTo(
|
||||
destIndex,
|
||||
transitionVelocity,
|
||||
@@ -829,11 +827,6 @@ var Navigator = React.createClass({
|
||||
return false;
|
||||
},
|
||||
|
||||
_resetUpdatingRange: function() {
|
||||
this.state.updatingRangeStart = 0;
|
||||
this.state.updatingRangeLength = this.state.routeStack.length;
|
||||
},
|
||||
|
||||
_getDestIndexWithinBounds: function(n) {
|
||||
var currentIndex = this.state.presentedIndex;
|
||||
var destIndex = currentIndex + n;
|
||||
@@ -851,15 +844,9 @@ var Navigator = React.createClass({
|
||||
|
||||
_jumpN: function(n) {
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
updatingRangeStart: destIndex,
|
||||
updatingRangeLength: 1,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
this._enableScene(destIndex);
|
||||
this._emitWillFocus(this.state.routeStack[destIndex]);
|
||||
this._transitionTo(destIndex);
|
||||
},
|
||||
|
||||
jumpTo: function(route) {
|
||||
@@ -891,18 +878,15 @@ var Navigator = React.createClass({
|
||||
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
|
||||
this.props.configureScene(route),
|
||||
]);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this._emitWillFocus(nextStack[destIndex]);
|
||||
this.setState({
|
||||
idStack: nextIDStack,
|
||||
routeStack: nextStack,
|
||||
sceneConfigStack: nextAnimationConfigStack,
|
||||
updatingRangeStart: nextStack.length - 1,
|
||||
updatingRangeLength: 1,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
}, () => {
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
});
|
||||
},
|
||||
|
||||
_popN: function(n) {
|
||||
@@ -915,6 +899,7 @@ var Navigator = React.createClass({
|
||||
);
|
||||
var popIndex = this.state.presentedIndex - n;
|
||||
this._enableScene(popIndex);
|
||||
this._emitWillFocus(this.state.routeStack[popIndex]);
|
||||
this._transitionTo(
|
||||
popIndex,
|
||||
null, // default velocity
|
||||
@@ -954,16 +939,15 @@ var Navigator = React.createClass({
|
||||
nextRouteStack[index] = route;
|
||||
nextAnimationModeStack[index] = this.props.configureScene(route);
|
||||
|
||||
if (index === this.state.presentedIndex) {
|
||||
this._emitWillFocus(route);
|
||||
}
|
||||
this.setState({
|
||||
idStack: nextIDStack,
|
||||
routeStack: nextRouteStack,
|
||||
sceneConfigStack: nextAnimationModeStack,
|
||||
updatingRangeStart: index,
|
||||
updatingRangeLength: 1,
|
||||
}, () => {
|
||||
this._resetUpdatingRange();
|
||||
if (index === this.state.presentedIndex) {
|
||||
this._emitWillFocus(route);
|
||||
this._emitDidFocus(route);
|
||||
}
|
||||
cb && cb();
|
||||
@@ -1034,69 +1018,17 @@ var Navigator = React.createClass({
|
||||
var newStackLength = index + 1;
|
||||
// Remove any unneeded rendered routes.
|
||||
if (newStackLength < this.state.routeStack.length) {
|
||||
var updatingRangeStart = newStackLength; // One past the top
|
||||
var updatingRangeLength = this.state.routeStack.length - newStackLength + 1;
|
||||
this.state.idStack.slice(newStackLength).map((removingId) => {
|
||||
this._itemRefs[removingId] = null;
|
||||
});
|
||||
this.setState({
|
||||
updatingRangeStart: updatingRangeStart,
|
||||
updatingRangeLength: updatingRangeLength,
|
||||
sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength),
|
||||
idStack: this.state.idStack.slice(0, newStackLength),
|
||||
routeStack: this.state.routeStack.slice(0, newStackLength),
|
||||
}, this._resetUpdatingRange);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_renderOptimizedScenes: function() {
|
||||
// To avoid rendering scenes that are not visible, we use
|
||||
// updatingRangeStart and updatingRangeLength to track the scenes that need
|
||||
// to be updated.
|
||||
|
||||
// To avoid visual glitches, we never re-render scenes during a transition.
|
||||
// We assume that `state.updatingRangeLength` will have a length during the
|
||||
// initial render of any scene
|
||||
var shouldRenderScenes = this.state.updatingRangeLength !== 0;
|
||||
if (shouldRenderScenes) {
|
||||
return (
|
||||
<StaticContainer shouldUpdate={true}>
|
||||
<View
|
||||
style={styles.transitioner}
|
||||
{...this.panGesture.panHandlers}
|
||||
onTouchStart={this._handleTouchStart}
|
||||
onResponderTerminationRequest={
|
||||
this._handleResponderTerminationRequest
|
||||
}>
|
||||
{this.state.routeStack.map(this._renderOptimizedScene)}
|
||||
</View>
|
||||
</StaticContainer>
|
||||
);
|
||||
}
|
||||
// If no scenes are changing, we can save render time. React will notice
|
||||
// that we are rendering a StaticContainer in the same place, so the
|
||||
// existing element will be updated. When React asks the element
|
||||
// shouldComponentUpdate, the StaticContainer will return false, and the
|
||||
// children from the previous reconciliation will remain.
|
||||
return (
|
||||
<StaticContainer shouldUpdate={false} />
|
||||
);
|
||||
},
|
||||
|
||||
_renderOptimizedScene: function(route, i) {
|
||||
var shouldRenderScene =
|
||||
i >= this.state.updatingRangeStart &&
|
||||
i <= this.state.updatingRangeStart + this.state.updatingRangeLength;
|
||||
var scene = shouldRenderScene ? this._renderScene(route, i) : null;
|
||||
return (
|
||||
<StaticContainer
|
||||
key={'nav' + i}
|
||||
shouldUpdate={shouldRenderScene}>
|
||||
{scene}
|
||||
</StaticContainer>
|
||||
);
|
||||
},
|
||||
|
||||
_renderScene: function(route, i) {
|
||||
var child = this.props.renderScene(
|
||||
route,
|
||||
@@ -1146,9 +1078,30 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var newRenderedSceneMap = new Map();
|
||||
var scenes = this.state.routeStack.map((route, index) => {
|
||||
var renderedScene;
|
||||
if (this._renderedSceneMap.has(route) &&
|
||||
index !== this.state.presentedIndex) {
|
||||
renderedScene = this._renderedSceneMap.get(route);
|
||||
} else {
|
||||
renderedScene = this._renderScene(route, index);
|
||||
}
|
||||
newRenderedSceneMap.set(route, renderedScene);
|
||||
return renderedScene;
|
||||
});
|
||||
this._renderedSceneMap = newRenderedSceneMap;
|
||||
return (
|
||||
<View style={[styles.container, this.props.style]}>
|
||||
{this._renderOptimizedScenes()}
|
||||
<View
|
||||
style={styles.transitioner}
|
||||
{...this.panGesture.panHandlers}
|
||||
onTouchStart={this._handleTouchStart}
|
||||
onResponderTerminationRequest={
|
||||
this._handleResponderTerminationRequest
|
||||
}>
|
||||
{scenes}
|
||||
</View>
|
||||
{this._renderNavigationBar()}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -76,15 +76,17 @@ class MessageQueue {
|
||||
* Public APIs
|
||||
*/
|
||||
processBatch(batch) {
|
||||
ReactUpdates.batchedUpdates(() => {
|
||||
batch.forEach((call) => {
|
||||
let method = call.method === 'callFunctionReturnFlushedQueue' ?
|
||||
'__callFunction' : '__invokeCallback';
|
||||
guard(() => this[method].apply(this, call.args));
|
||||
guard(() => {
|
||||
ReactUpdates.batchedUpdates(() => {
|
||||
batch.forEach((call) => {
|
||||
let method = call.method === 'callFunctionReturnFlushedQueue' ?
|
||||
'__callFunction' : '__invokeCallback';
|
||||
guard(() => this[method].apply(this, call.args));
|
||||
});
|
||||
BridgeProfiling.profile('ReactUpdates.batchedUpdates()');
|
||||
});
|
||||
BridgeProfiling.profile('ReactUpdates.batchedUpdates()');
|
||||
BridgeProfiling.profileEnd();
|
||||
});
|
||||
BridgeProfiling.profileEnd();
|
||||
return this.flushedQueue();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user