触摸事件的分发机制

ViewGroup的事件分发机制


View一般都包含在ViewGroup中,例如在actingity中触摸事件一般从window、activity、decorView、ViewGroup,最后才是被点击的空间,所以其中的触摸事件分发机制就必须搞清楚。


View的事件分发方法:
1、dispatchTouchEvent()
2、onTouch()
3、onTouchEvent()

/**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (!onFilterTouchEventForSecurity(event)) {
            return false;
        }

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);
    }

根据源码可知,如果该View有触摸监听事件并且是可点击的控件,View的onTouch()时事件就会被触发,之后的onTouchEvent()事件就不会被触发。而且view的触摸监听事件其实就是view的setonTouchListener()。也就是说:如果我们设置了setOnTouchListener,并且return true控件是ENABLE状态,
最重要的mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。也就是说:dispatchTouchEvent()中最先执行的是onTouch()方法,如果该方法返回True,onTouchEvent()方法将不会被执行。只有返回值为FALSE时,onTouchEvent()将会被执行时,onTouchEvent中的DOWN,MOVE,UP触摸事件操作

DOWN时:
a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;
b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;
c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:
此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

MOVE时:
主要就是检测用户是否划出控件,如果划出了:
115ms内,直接移除mPendingCheckForTap;
115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

UP时:
a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;
b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;
c、如果是500ms以后,那么有两种情况:
i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;
ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;
d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;


ViewGroup的触摸事件分发方法

  • dispatchTouchEvent 事件分发

  • InterceptTouchEvent 事件拦截

  • OnTouchEvent 事件消费

ViewGroup通过DispatchTouchEvent()处理该事件的分发,它首先调用自身的InteceptTouchEvent()方法,判断当前viewGroup是否对当前事件进行拦截,如果为True,则表示当前VIewGroup将改事件消费掉,不会将事件继续传递给子View,如果为False,则调用子view的DispatchTouchEvent(),子控件如果是单纯的View的话,他无法继续传递,所以只能传递给自身的ONTouchEvent()方法,如果OnTouchEvent()方法返回True,则表示当前事件被消费,触摸事件传递终止。如果为False,则当前子VIew不对该事件进行处理,事件将通过之前的传递路径往回传递。

ViewGroup的onInteceptTouchEvent()方法默认返回False,即ViewGroup默认不拦截触摸事件。


触摸事件的三种方式的事件处理流程大致可归结为一下结论:

1、ACTION_DOWN中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则找到包含当前x,y坐标的子View,赋值给mMotionTarget,然后调用 mMotionTarget.dispatchTouchEvent()进行事件处理,Target可以是view或是ViewGroup,当前ViewGroup的OnTouchEvent()方法不会执行。

2、ACTION_MOVE中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)

3、ACTION_UP中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev),当然了在分发之前都会修改下坐标系统,把当前的x,y分别减去child.left 和 child.top ,然后传给childView;
4、一般ViewGroup不会对事件进行拦截处理,但是如果他真的拦截的话,子View可以调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值