android 事件分发机制

1.其实就是我们在Activity中设置的setOnTouchListener。

也就是说:如果我们设置了setOnTouchListener,并且return true,那么自定义View自己的onTouchEvent就不会被执行了,当然了,本例我们return false,我们还得往下探索 ;

已经解决一个常见的问题:View的onTouchListener和onTouchEvent的调用关系

2.

View的onTouchEvent:

1、整个View的事件转发流程是:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。

...................................................................................---------------------------------


1、如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;

2、可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法

3、子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true);  阻止ViewGroup对其MOVE或者UP事件进行拦截;


好了,那么实际应用中能解决哪些问题呢?

比如你需要写一个类似slidingmenu的左侧隐藏menu,主Activity上有个Button、ListView或者任何可以响应点击的View,你在当前View上死命的滑动,菜单栏也出不来;因为MOVE事件被子View处理了~ 你需要这么做:在ViewGroup的dispatchTouchEvent中判断用户是不是想显示菜单,如果是,则在onInterceptTouchEvent(ev)拦截子View的事件;自己进行处理,这样自己的onTouchEvent就可以顺利展现出菜单栏了~~


-------------------------------------------------------------------
1.自定义view中重写的 方法
 -onTouchEvent()----返回true代表,我要处理这个事件,就不传递了,我自己独享,返回false的话,事件向父view传递
-( DOWN事件的处理实际上经历了一下一上两个过程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,当然,任意一步的方法中返回true,都能阻止它继续传播)-
-dispatchOnTouchEvent()
-用它的时候写的方法
.setOnTouchListenter()

2. 自定义viewGroup中重写的 方法
-dispatchTouchEvent();
-onTouchEvent()

-onInterceptTouchEvent();

---返回false表示父view不想拦截,交给子view处理,子view的dispathOnTouch()处理
--返回true,父view拦截,交给自己的onTouchEvent()处理

3.activity中重写的方法

-dispatchTouchEvent();

-onTouchEvent();


-----------------------------------------------------=======================================================

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

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

  onInterceptTouchEvent() 方法可以将触摸事件的传递截断,让触摸事件在某一层就不往下面传递,就开始调用这一层的dispatchTouchEvent(),开始向上层返回。如果需要在某一层拦截,需要复写该层的onInterceptTouchEvent()方法,并让该方法返回 true。通过一张图来解释一下。

  

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

  

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

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

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

  结合上面的分析,我们可以知道,如果让触摸事件传递到内层的图片,然后在在图片的disPatchTouchEvent()中把这个触摸事件消费了就不就可以了吗?

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

  问题又来了 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()会调用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值