android scrollview的所有touchevent的顺序

最近再写一个下拉的scrollview,里边要处理比较复杂的手势,发现自己这方面很迷茫,于是自己用log总结了一下

一个scrollview有

dispatchTouchEvent

onInterceptTouchEvent

OnTouchListener(已设置)

onTouchEvent

fling

onScrollChanged

这些手势的顺序是



1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

1、如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,    如果最终需要处理事件的viewonTouchEvent()返回了false,那么该事件将被传递至其上一层次的viewonTouchEvent()处理。如果最终需要处理事件的view onTouchEvent()返回了true,那么后续事件将可以继续传递给该viewonTouchEvent()处理。

2、如果dispatchTouchEvent返回 false ,则交给这个 view的interceptTouchEvent方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是表示拦截掉了,则交给它的 onTouchEvent 来处理,

3、如果 interceptTouchEvent 返回 false ,那么就传递给子view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。

4、如果事件传递到某一层的子 view 的onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个view 往上传递,由父view的 onTouchEvent 来接收。

5、如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。


但是如果设置了ontouchelistener之后呢?

onTouchListener的接口的优先级是要高于onTouchEvent的,假若onTouchListener中的onTouch方法返回true,表示此次事件已经被消费了,那onTouchEvent是接收不到消息的。

接着是fling方法,要注意的是,一定要在scrollview的ontouevent中调用父类的ontouchevent方法,要不然scrollview不能滑动,查看源码可以知道,在这里面调用了scrollview的fling方法,fling方法只执行一遍,fling意味“甩”,意思就是当用户大力向下滑,接着松手,就会调用fling函数,参数为应该滑动的距离

            case MotionEvent.ACTION_UP:
                if (mIsBeingDragged) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                     int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
 
                     if (getChildCount() > 0) {
                         if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                             fling(-initialVelocity);
                         } else {
                            if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
                                     getScrollRange())) {
                                 postInvalidateOnAnimation();
                             }
                         }
                     }
 
                     mActivePointerId = INVALID_POINTER;
                     endDrag();
                }
                 break;

所以最好再scrollview中使用ontouchlistener

最后是onscrollchanged的方法,这是再ontouchevent里面的代码

// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)) {
// Break our velocity if we hit a scroll barrier.
   mVelocityTracker.clear();
}

可见onscrollchanged和fling方法都是再scrollview的ontouchevent的方法里面调用的,如果ontouchevent方法中不调用父类的ontouchevent方法,则onscrollchanged方法和ontouchevent方法都不会调用的


关于点击事件的机制,这里给出一些结论,根据这些结论可以更好地理解整个传递机制,如下所示:

(1)同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列的事件,这个事件序列以down事件开始,中间含有数量不定的move事件,可以为0,最终以up事件结束。


(2)正常情况下,一个事件序列只能被一个View拦截且消耗。这一条的原因可以参考(3),因为一旦一个元素拦截了某些事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。


(3)某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent不会被调用。这条也很好理解,就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用再调用这个View的onInterceptTouchEvent去询问它是否要拦截了。


(4)某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一事件序列中的其他事件都不会再交给它来处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。意思就是事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给它来处理了。


(5)如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。


(6)ViewGroup默认不拦截任何事件,ViewGroup源码的onInterceptTouchEvent方法默认返回false。


(7)View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。


(8)View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longclickable同时为false)。View的longClickable属性默认都是false,cilckable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。


(9)View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态的,只要它的clickable或者longclickable有一个为true,那么它的onTouchEvent就返回true。


(10)onClick会发生的前提是当前View是可点击的,并且它收到了down和up事件。


(11)事件传递过程是由内向外的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。


更新:今天写一个东西的时候,发现在ontouchevent里面捕捉不到down事件,后来找到原因是由于ontouchevent的函数又从子控件向父控件的方向执行,当子控件的down事件返回true,将down事件截取,但是后续事件返回false或者recycle,接着父控件就捕捉到了这个触摸事件的后续事件,当然是残缺的,所以这类事件最好应该再oninterceptTouchevent或者dispatchtouchevent里面执行


更新:父控件通过oninterceptTouchEvent截取了这个touchevent(注意,之后oninterceptTouchEvent返回true之后就不会再次调用了),那么以后的touchevent就会由父控件的ontouchevent进行处理,但是此时如果父控件的ontouchevent返回false,那么这个touchevent就交给再上一层的父控件进行处理,如果上一层控件不处理,则改touchevent直接抛弃


更新:如果子控件的onTouchevent返回true,代表要截取这个touchevent以后的所有action,但是父控件还是可以通过oninterceptTouchevent来决定截取否,但是再实现过程中发现了一个问题,就是父控件的oninterceptTouchevent不执行,这就是因为子控件调用了getParent().requestDisallowInterceptTouchEvent(true)方法.一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action(这类控件:继承absListView的子view)


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值