Android 触摸事件处理机制

  Android 触摸事件的处理主要涉及到几个方法:onInterceptTouchEvent(), dipatchTouchEvent(), onTouchEvent(), onTouch()。this

  onInterceptTouchEvent() 用于拦截事件并改变事件传递方向。解释一下事件传递。好比一个Activity中展现给用户多是ViewGroup和View的多层嵌套,默认状况下触摸事件产生以后从最外层一次传递到最里面一层,而后在从最里面一层开始响应。从最里面一层开始依次调用各层次的dispatchTouchEvent()进行分发,dispatchTouchEvent()中在调用onTouch / onTouchEvent进行响应触摸事件。spa

  onInterceptTouchEvent() 方法能够将触摸事件的传递截断,让触摸事件在某一层就不往下面传递,就开始调用这一层的dispatchTouchEvent(),开始向上层返回。若是须要在某一层拦截,须要复写该层的onInterceptTouchEvent()方法,并让该方法返回 true。经过一张图来解释一下。
rest

  

  假设一个Activity展现的界面有A->B->C->D四层,当事件发生以后,首先通过A的onInterceptTouchEvent(), 若是A的onInterceptTouchEvent()返回false,则会传递到B的onInterceptTouchEvent(),若是返回false则一次向下(内层)传递。若是某一层的onInterceptTouchEvent()返回true,而后就会调用该层的disatchTouchEvent()分发事件,事件再也不向下传递。若是各层都没有拦截事件则从最内层开始调用dispatchTouchEvent(),若是某一各层的dispatchTouchEvent()返回true,则代表该层消费了该事件,则上面层的dispatchTouEvent()不会被调用。举一个例子:code

  

  上图中B层的onInterceptTouchEvent()返回true,则事件被拦截,开始调用B层的dispatchTouchEvent()向上返回一次响应触摸事件。orm

  知道这个机制有什么卵用吗?blog

  一个简单例子,咱们在scrollView中放置了图片,图片容许缩放拖动,可是你对图片进行拖动的时候会发现scrollView也跟着动,这样体验就会很很差,怎么办呢?事件

  结合上面的分析,咱们能够知道,若是让触摸事件传递到内层的图片,而后在在图片的disPatchTouchEvent()中把这个触摸事件消费了就不就能够了吗?图片

  具体作法在图片的onTouch() 方法中,ACTION_DOWN中设置scrollView不拦截事件,经过scrollView.requestDisallowInterceptTouchEvent(true)来完成,完成想要的处理以后在图片的onTouch()方法最后返回true就能够实现了。ip

  问题又来了 onTouch(View v, MotionEvent event) 和 onTouchEvent(MotionEvent event) 有什么区别呢? 看起来那么像,不会是完成相同的功能吧?这样作不是画蛇添足吗,为何不用一个就行了。开发

  为了搞清楚这个问题,首先须要来看View中disPatchTouchEvent()方法:

public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first.
    if (event.isTargetAccessibilityFocus()) { // We don't have focus or no virtual descendant has it, do not handle the event.
        if (!isAccessibilityFocusedViewOrHost()) { return false; } // We have focus and got the event, then use normal event dispatch.
        event.setTargetAccessibilityFocus(false); } boolean result = false; if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { // Defensive cleanup for new gesture
 stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture.
    if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }

  从上面能够看出onTouch() 先于 onTouchEvent()执行。经过查看View的源码还发现,或者简单推理一下咱们设置setOnTouchListener()设置的是谁就知道,咱们何时须要调用onTouch()方法让其发挥做用而不是让onTouchEvent()来响应了。View源码中能够看出onTouch是一个Interface的方式实现,将处理逻辑的实现交给开发者自定义,所以能够得出Android系统自带的控件咱们使用onTouch处理事件,若是咱们须要扩展View,则须要复写onTouchEvent()来实现事件的处理。其实onTouch(View v, MotionEvent event) 和 onTouchEvent(MotionEvent event)能够从这两个方法接受的参数就能够看出不一样来,onTouch包含一个View类型的参数,所以是能够设置给某个View的,onTouchEvent()则是给某个View本身用的。

  既然提到了View的事件响应,那onClick事件又是怎么响应的呢? 经过产看源码能够发现onClick是在onTouchEvent中执行的,并且是在onTouchEvent的ACTION_UP事件中执行的。所以若是View 的onTouch()返回true则会致使onClick得不到执行,由于onTouchEvent()得不到执行。

  此外Activity中也有onTouchEvent()成员方法,若是Activity中的View都不处理Event则Activity的onTouchEvent()会调用。

相关文章
相关标签/搜索