Gesture recognition is much more complex on a mobile device than on a network. A touch can go through several stages when the application determines the user's intentions.
For example, the application needs to determine whether the touch is scrolling , sliding parts or tapping , zooming on the map . This can even change during touch, or there can be multiple simultaneous touches .
To enable a component to handle these touch interactions without any additional knowledge about their parent components or subcomponents, a touch answering system is required. This system is implemented in Respondereventplugin.js, which contains more details and documentation.
Best practices
Users can feel a huge difference in the usability of the Web app and the native app, and that's one of the biggest reasons. Each action should have the following properties:
- Feedback/Highlighting – shows what the user is doing with their touch, and what happens when they release the gesture
- Ability to undo – as an action, the user should be able to abort the action by moving the finger during the touch
These features make it more comfortable for users to use an application because it allows people to experiment and interact without worrying about making mistakes.
Touchablehighlight and touchable*
The response system may be complex to use. So we provide an abstract touchable implementation for something that should be "lightly clicked". This uses the answering system and makes it easy to identify the tap interaction in a declarative way. Use Touchablehighlight anywhere in the network where you will be using buttons or links.
Responder life cycle
By implementing the correct processing method, the view can become a contact responder. There are two ways to ask whether a view wants to be a responder:
-View.props.onStartShouldSetResponder: (evt) = True,—— does this view want to be a responder when the touch starts?
-View.props.onMoveShouldSetResponder: (evt) = True,—— when the view is not a responder, the directive is called by a touch that moves on the view: does this view want to "declare" the touch response?
If the view returns true and wants to be a responder, one of the following occurs:
-View.props.onResponderGrant: (evt) = > {}--View is now responding to touch events. This is the time to highlight and show what is happening to the user.
-View.props.onResponderReject: (evt) = > {}--At present there is something else that becomes a responder and does not release it.
If the view is responding, the following handlers can be called:
-View.props.onResponderMove: (evt) = > {}--user is moving their finger
-View.props.onResponderRelease: (evt) = > {}--is thrown at the end of the touch, i.e. "touchUp"
-View.props.onResponderTerminationRequest: (evt) = >true--Other things want to be a responder. Should this view release an answer? Returns true to allow free
-View.props.onResponderTerminate: (evt) = > {}--Responder has been drained from this view. It may be obtained by other views after calling Onresponderterminationrequest, or it may have been acquired by the operating system without a request (in Control Center/notification center of IOS).
EVT is an integrated touch event that has the following form:
-Changedtouches: Since the last event, the array of all touch events that changed
-The ID of the identifier--touch
-locationx--touch relative to the X position of the element
-locationy--touch relative to the Y position of the element
-pagex--touch relative to the X position of the screen
-pagey--touch relative to the Y position of the screen
-target--Node ID of the element that receives the touch event
-timestamp--Touch time identifier for speed calculation
-touches--all the currently touching arrays on the screen
Capturing Shouldset Handlers
Onstartshouldsetresponder and Onmoveshouldsetresponder are called in bubbling mode, where the deepest nodes are first called. This means that when multiple views return true for the * Shouldsetresponder handler, the deepest component becomes the responder. In most cases, this is desirable because it ensures that all controls and buttons are available.
However, sometimes the parent component wants to make sure that it becomes a responder. This can be handled by using the capture phase. When the answering system bubbles from the deepest component, it will carry out a capture phase that raises * shouldsetrespondercapture. So if a parent view is to prevent a child view from becoming a responder at the start of a touch, it should have a onstartshouldsetrespondercapture handler that returns True.
-View.props.onStartShouldSetResponderCapture: (evt) = True,
-View.props.onMoveShouldSetResponderCapture: (evt) = True,
Panresponder Panorama Responder
The panresponder adjusts several triggers into a single trigger action. This method can make the single triggering action elastic to the additional trigger and can be used to identify the simple multi-point triggering action.
It provides a predictable package for the response handler, which is provided by the action answering system. For each handler, a new Gesturestate object is provided next to the normal event. A Gesturestate object has the following properties:
-Stateid-gesturestate's id-keeps at least one trigger action on the screen
-Latest screen coordinates movex-recently dynamically triggered
-horizontal screen coordinates of the x0-transponder
-vertical screen coordinates of the y0-transponder
-dx-the horizontal action distance accumulated after the start of the trigger
-The longitudinal motion distance accumulated after the start of the dy-trigger
-vx-The horizontal speed of the current gesture
-vy-The vertical speed of the current gesture
-The number of current triggers on the numberactivetouch-screen
Basic usage
componentWillMount: function() {
this._panResponder = PanResponder.create({
// Ask to be the responder:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
// The guesture has started. Show visual feedback so the user knows
// what is happening!
// gestureState.{x,y}0 will be set to zero now
},
onPanResponderMove: (evt, gestureState) => {
// The most recent move distance is gestureState.move{X,Y}
// The accumulated gesture distance since becoming responder is
// gestureState.d{x,y}
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
// The user has released all touches while this view is the
// responder. This typically means a gesture has succeeded
},
onPanResponderTerminate: (evt, gestureState) => {
// Another component has become the responder, so this gesture
// should be cancelled
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// Returns whether this component should block native components from becoming the JS
// responder. Returns true by default. Is currently only supported on android.
return true;
},
});
},
render: function() {
return (
<View {...this._panResponder.panHandlers} />
);
},
Method:
-
- Static Create (Config:object)
@param {Object} is an enhanced version of all responder callback configurations, the responder callback can provide not only a typical respondersyntheticevent, but also a Panresponder action state. In each typical onresponder* callback, a simple substitution is made with Panresponder for Responder. For example, the Config object might look like the following form:
-
- Onmoveshouldsetpanresponder: (e, gesturestate) = {...}
-
- Onmoveshouldsetpanrespondercapture: (e, gesturestate) = {...}
-
- Onmoveshouldsetpanrespondercapture: (e, gesturestate) = {...}
-
- Onstartshouldsetpanresponder: (e, gesturestate) = {...}
-
- Onstartshouldsetpanrespondercapture: (e, gesturestate) = {...}
-
- Onpanresponderreject: (e, gesturestate) = {...}
-
- Onpanrespondergrant: (e, gesturestate) = {...}
-
- Onpanresponderstart: (e, gesturestate) = {...}
-
- Onpanresponderend: (e, gesturestate) = {...}
-
- Onpanresponderrelease: (e, gesturestate) = {...}
-
- Onpanrespondermove: (e, gesturestate) = {...}
-
- Onpanresponderterminate: (e, gesturestate) = {...}
-
- Onpanresponderterminationrequest: (e, gesturestate) = {...}
In general, for those equivalent events captured, we update the gesturestate once during the capture phase and can also be used during the bubbling phase.
There is a point to be aware of when onstartshould* callbacks. In the start/end events of the capture/bubbling phase of the node, they respond only to the updated gesturestate. Once the node becomes the transponder, you can rely on each of the start/end events that are updated after the action and gesturestate are processed. (numberactivetouches) may not be completely accurate unless you are the transponder.
Instance:
‘use strict‘;
import React, { Component } from ‘react‘;
import {
PanResponder,
StyleSheet,
View,
Text,
processColor,
} from ‘react-native‘;
var CIRCLE_SIZE = 80;
export default class PanResponderMazouri extends Component {
constructor(props) {
super(props);
this._handleStartShouldSetPanResponder = this._handleStartShouldSetPanResponder.bind(this);
this._handleMoveShouldSetPanResponder = this._handleMoveShouldSetPanResponder.bind(this);
this._handlePanResponderGrant = this._handlePanResponderGrant.bind(this);
this._handlePanResponderMove = this._handlePanResponderMove.bind(this);
this._handlePanResponderEnd = this._handlePanResponderEnd.bind(this);
this._handlePanResponderEnd = this._handlePanResponderEnd.bind(this);
this._highlight = this._highlight.bind(this);
this.state = {
_panResponder: {},
_previousLeft: 0,
_previousTop: 0,
backgroundColor: ‘green‘,
}
}
componentWillMount() {
console.log("MAZOURI_LOG componentDidMount");
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
});
this.setState({
_previousLeft: 20,
_previousTop: 84,
left: 20,
top: 84,
backgroundColor: ‘green‘,
});
}
componentDidMount() {
console.log("MAZOURI_LOG componentDidMount");
}
render() {
console.log("MAZOURI_LOG left:"+ this.state.left + "top:" + this.state.top + "backgroundColor:" + this.state.backgroundColor);
return (
<View style={styles.container}>
<View
ref={circle => this.circle = circle}
style={[styles.circle, {left: this.state.left, top: this.state.top, backgroundColor: this.state.backgroundColor}]}
{...this._panResponder.panHandlers}
/>
</View>
);
}
_handleStartShouldSetPanResponder(e, gestureState) {
console.log("MAZOURI_LOG _handleStartShouldSetPanResponder");
return true;
}
_handleMoveShouldSetPanResponder(e, gestureState) {
console.log("MAZOURI_LOG _handleMoveShouldSetPanResponder");
return true;
}
_handlePanResponderGrant(e, gestureState) {
console.log("MAZOURI_LOG _handlePanResponderGrant");
this._highlight;
}
_handlePanResponderMove(e, gestureState) {
console.log("MAZOURI_LOG _handlePanResponderMove");
var _left = this.state._previousLeft + gestureState.dx;
var _top = this.state._previousTop + gestureState.dy;
this.setState({
left: _left,
top: _top,
});
}
_handlePanResponderEnd(e, gestureState) {
console.log("MAZOURI_LOG _handlePanResponderEnd");
this._unHighlight;
var _previousLeft = this.state._previousLeft + gestureState.dx;
var _previousTop = this.state._previousTop + gestureState.dy;
this.setState({
_previousLeft: _previousLeft,
_previousTop: _previousTop,
});
}
_highlight() {
this.setState({
backgroundColor: ‘blue‘,
});
}
_unHighlight() {
this.setState({
backgroundColor: ‘green‘,
});
}
}
var styles = StyleSheet.create({
circle: {
width: CIRCLE_SIZE,
height: CIRCLE_SIZE,
borderRadius: CIRCLE_SIZE / 2,
position: ‘absolute‘,
left: 0,
top: 0,
},
container: {
flex: 1,
paddingTop: 64,
},
});
[In-depth anatomy react Native] gesture response explanation