react-native ScrollView触摸与滚动事件

来源:互联网 发布:软件项目总结ppt 编辑:程序博客网 时间:2024/06/02 13:37

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)onScrollBeginDrag
拖拽开始,子视图开始移动,只是开始时回去调用。

(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
最后是一帧滚动的结束,惯性滚动结束,屏幕静止。

未完待续(事件抢夺)

原创粉丝点击