关于滚动事件的处理
由于曝光事件是在滚动停止时产生,因此需要分析滚动何时停止,具体来讲,有以下几种情况:
1.快速拖拽后放开,然后页面继续滚动一段距离,动画结束后自然停止。这种情况会先触发onScrollEndDrag(可能不止一次,需要判断滑动速度是否为0),然后再触发 onMomentumScrollBegin(滚动动画开始)和 onMomentumScrollEnd(滚动动画结束)。如果我们在onScrollEndDrag触发时生成曝光事件是不对的,因为这时候滚动还没停止,曝光的元素不对。正确的时机应该是在onMomentumScrollEnd时或者是contentOffset不再变化时。
2.缓慢拖拽后放开,页面滚动立即停止。这种情况onScrollEndDrag的时候就是滚动停止的时候,不会触发onMomentumScrollBegin和onMomentumScrollEnd。 我们需要在onScrollEndDrag滑动速度为0时生成曝光事件。
3.快速拖拽后放开,然后页面继续滚动一段距离,在动画还未结束前再用手按住屏幕停下。这种情况也会触发onScrollEndDrag和onMomentumScrollBegin/ onMomentumScrollEnd,其中onScrollEndDrag会触发两次(第一次拖拽停止,第二次用手按住屏幕)。我们需要在第二次触发onScrollEndDrag或者是onMomentumScrollEnd触发时生成曝光事件。
4.通过其他按钮触发了scrollTo方法,导致滑动。这种情况不会触发onScrollEndDrag,scrollTo也不会一直调用。但是onMomentumScrollEnd会调用。
由于情况1和情况2 是互斥的,所以我们需要一个标志位或者记录拖拽结束时scrollView的位移来判断是在哪个滚动事件里面处理曝光。
如果是触发了onMomentumScrollBegin,可以认为onScrollEndDrag是不应该处理曝光的,但onMomentumScrollBegin在onScrollEndDrag之后触发。这时候有三种方案:
一、 在onMomentumScrollBegin 修改标志位,onScrollEndDrag触发时标志位并没有发生变化,所以需要在onScrollEndDrag里面延迟一小段时间进行曝光(如100-200毫秒,需要大于两次事件之间的间隔),在时间到之前如果标志位发生变化,则onScrollEndDrag放弃处理,交给onMomentumScrollEnd去处理。
二、在onScroll里面判断contentOffset有没有变化,如果有变化则认为滚动还没有停止,把实时的位移保存下来,并跟onScrollEndDrag触发时的位移做比较。如果两个位移一致,则认为拖拽结束后没有继续滚动,可以在onScrollEndDrag产生曝光,如果不一致,则认为继续滚动,需要在onScroll里面处理曝光。这里也需要在onScrollEndDrag延迟一小段时间进行曝光,因为需要监听拖拽停止后一段时间内位移有没有变化。
三、结合方案一和方案二,通过onScroll来的瞬时位移与onScrollEndDrag的位移来判断拖拽结束后是否还继续滚动。 onScroll里面不处理曝光,改为在onMomentumScrollEnd里面处理曝光。
这里我们采用方案三,可以少处理一些事件(onScroll是本来就要处理的),同时也兼顾没有拖拽这种情况
然后再考虑情况3,如果用上面的方式处理,在onScrollEndDrag第二次触发到延迟上报之前,滚动停止了并且产生一次曝光,然后定时器到了之后又产生一次,相当于在较短的时间内产生了两次曝光,这是不合理的。所以需要再添加一个标记位或者是通过防抖函数进行处理,这个防抖的时间间隔需要大于定时器的间隔。
网友评论