最近再写一个下拉的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()处理。如果最终需要处理事件的viewonTouchEvent()返回了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. Android(安卓)lifecycle 实战及使用进阶
  2. Android优秀开源库1000+,你值得拥有
  3. 转发:bitmap 设置图片尺寸,避免 内存溢出 OutOfMemoryError的优化
  4. Android(安卓)UI 学习 自定义的布局 平滑移动 VelocityTracker()
  5. Android____View-->Bitmap
  6. android注入代码之注入类方法
  7. android使用activitygroup和Scrollview的方法
  8. android studio中gradle更新办法
  9. Android中获取文件路径的方法总结及对照

随机推荐

  1. Android智能手机搜索不到路由器无线信号
  2. 关于android的广播接收器(1)—基础篇
  3. Android的消息机制之Handler源码解析
  4. mac 系统下android源码下载以及使用(总结)
  5. Android NDK开发学习(一)
  6. Android通过adb查看wifi密码
  7. Android应用程序内部启动Activity过程(sta
  8. Google 推出 Android Design 網站,教您如
  9. 手机或android设备连接电脑,但是adb devic
  10. Android 开机速度优化-----ART 预先优化