您的位置:首页 > Web前端 > React

react-native ScrollView触摸与滚动事件

2017-08-29 16:51 513 查看
ScrollView是我们常用的组件之一,因此搞清楚它的触摸与滚动事件十分重要!

1.在ScrollView里面轻触一下



(1)onStartShouldSetResponderCapture

这个属性接收一个回调函数,函数原型是 function(evt): bool,在触摸事件开始(touchDown)的时候,RN 容器组件会回调此函数,询问组件是否要劫持事件响应者设置,自己接收事件处理,如果返回 true,表示需要劫持;

/**
* There are times when the scroll view wants to become the responder
* (meaning respond to the next immediate `touchStart/touchEnd`), in a way
* that *doesn't* give priority to nested views (hence the capture phase):
*
* - Currently animating.
* - Tapping anywhere that is not the focused input, while the keyboard is
*   up (which should dismiss the keyboard).
*
* Invoke this from an `onStartShouldSetResponderCapture` event.
*/
scrollResponderHandleStartShouldSetResponderCapture: function(e: Event): boolean {
// First see if we want to eat taps while the keyboard is up
var currentlyFocusedTextInput = TextInputState.currentlyFocusedField();
if (!this.props.keyboardShouldPersistTaps &&
currentlyFocusedTextInput != null &&
e.target !== currentlyFocusedTextInput) {
return true;
}
return this.scrollResponderIsAnimating();
},


也就是说,我们在触碰ScrollView的时候,这个方法是第一个调用的,目的是判断是否进行劫持这个触摸事件(true是拦截,不让子视图去处理触摸事件;false是放开,交给子视图处理本次触摸事件)。目前,这个方法里面只对两种情况进行了处理,一是,屏幕内是否有TextInput正处于focused状态,如果是,则拦截,交给ScrollView去处理(例如:我们在ScrollView里面使用了TextInput,而此时正处于focused状态,我们点击ScrollView的其他区域则响应的应该是滑动事件);二是,判断现在动画是否正在进行(true是正在进行,false是没有正在进行的动画)。

注意:如果将这个函数的返回值,一直保持的是true,那么所有的触摸事件将不会下发给子视图,也就是说只可以响应ScrollView的触摸与滚动事件。

(2)onStartShouldSetResponder

这个属性接收一个回调函数,函数原型是 function(evt): bool,在触摸事件开始(touchDown)的时候,RN 会回调此函数,询问组件是否需要成为事件响应者,接收事件处理,如果返回 true,表示需要成为响应者;

假如组件通过上面的方法返回了 true,表示发出了申请要成为事件响应者请求,想要接收后续的事件输入。因为同一时刻,只能有一个事件处理响应者,RN 还需要协调所有组件的事件处理请求,所以不是每个组件申请都能成功,RN 通过如下两个回调来通知告诉组件它的申请结果。也就是说,只是去询问你想不想成为事件响应者,具体能不能成功,要看下面函数。

/**
* Merely touch starting is not sufficient for a scroll view to become the
* responder. Being the "responder" means that the very next touch move/end
* event will result in an action/movement.
*
* Invoke this from an `onStartShouldSetResponder` event.
*
* `onStartShouldSetResponder` is used when the next move/end will trigger
* some UI movement/action, but when you want to yield priority to views
* nested inside of the view.
*
* There may be some cases where scroll views actually should return `true`
* from `onStartShouldSetResponder`: Any time we are detecting a standard tap
* that gives priority to nested views.
*
* - If a single tap on the scroll view triggers an action such as
*   recentering a map style view yet wants to give priority to interaction
*   views inside (such as dropped pins or labels), then we would return true
*   from this method when there is a single touch.
*
* - Similar to the previous case, if a two finger "tap" should trigger a
*   zoom, we would check the `touches` count, and if `>= 2`, we would return
*   true.
*
*/
scrollResponderHandleStartShouldSetResponder: function(): boolean {
return false;
},


这个方法全部返回false,在这个地方基本没起到作用,因为轻触一下屏幕还不足以让它成为事件响应者(毕竟主要功能是滚动),看注释的意思是后期扩展使用!

(3)onTouchStart

按下屏幕时触发,即使现在屏幕还在滚动或者有其他手指又触发屏幕。

(4)onTouchEnd

手指离开屏幕触摸结束时触发,即使现在屏幕还在滚动,跟onTouchStart相反。

2.滚动ScrollView



手指抬起之前

前面散步已经说过了,就不过多介绍了。

(1)onTouchMove

移动手指时触发,表示触摸手指移动的事件,这个回调可能非常频繁,所以这个回调函数的内容需要尽量简单;可以观察一下,这个过程中一共触发两轮onTouchMove方法,第一轮,指的是手指发生了小范围的移动,但是不足以触发屏幕滚动;第二轮,是真正的视图滚动。总之,只要手指发生偏移量,这个方法就会被回调。

(2)onScrollBeginDr
4000
ag


拖拽开始,子视图开始移动,只是开始时回去调用。

(3)onScrollShouldSetResponder

跟前面onStartShouldSetResponder相类似,询问组件是否需要成为滚动事件响应者,接收事件处理,如果返回 true,表示需要成为响应者;

/**
* Invoke this from an `onScroll` event.
*/
scrollResponderHandleScrollShouldSetResponder: function(): boolean {
return this.state.isTouching;
},


isTouching指的是当前ScrollView区域是否还有触摸点。

(4)onResponderGrant

表示申请成功,组件成为了事件处理响应者,这时组件就开始接收后序的滚动事件输入。一般情况下,这时开始,组件进入了激活状态,并进行一些事件处理或者手势识别的初始化。

(5)onScroll(_handleScroll)

也许在这些方法中,我们最关心,也是最容易使用到的就是在这个方法了。像平时我们需要ScrollView的滑动来操作某些动画或者其他情况的,依靠的就是这个方法。

_handleScroll: function(e: Object) {
console.log('***********_handleScroll');
if (__DEV__) {
if (this.props.onScroll && !this.props.scrollEventThrottle && Platform.OS === 'ios') {
console.log( // eslint-disable-line no-console-disallow
'You specified `onScroll` on a <ScrollView> but not ' +
'`scrollEventThrottle`. You will only receive one event. ' +
'Using `16` you get all the events but be aware that it may ' +
'cause frame drops, use a bigger number if you don\'t need as ' +
'much precision.'
);
}
}
if (Platform.OS === 'android') {
if (this.props.keyboardDismissMode === 'on-drag') {
dismissKeyboard();
}
}
this.scrollResponderHandleScroll(e);
},


注意:注释部分人家也说了,如果你没有设置scrollEventThrottle这个属性,那么onScroll这个方法只是回调一次,如果设置的16(js:60帧每秒,每帧大概16ms),那么会引起丢帧的问题,总之选一个合适的值。



手指抬起之后(红框里面)

(1)onResponderRelease

手指释放后,视图成为响应者,释放滚动事件。

(2)onScrollEndDrag

滑动结束拖拽时触发,并不一定是停止滚动。

(3)onMomentumScrollBegin

接着就是一帧滚动的开始onMomentumScrollBegin,它的起始位置和onScrollEndDrag的结束位置重合。(惯性滚动)

(4)onScrollShouldSetResponder

因为这个时候,所有手指全部抬起来了,所以返回值一直就是false,则ScrollView不想成为滚动事件响应者,更不存在下面的那些流程了。

(5)onScroll(_handleScroll)

滚动并没有停止,坐标一直在变化,所以还会回调。



注:设置scrollEventThrottle这个属性,onScrollShouldSetResponder和onScroll频繁回调。

(6)onMomentumScrollEnd

最后是一帧滚动的结束,惯性滚动结束,屏幕静止。

未完待续(事件抢夺)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: