您的位置:首页 > 其它

触摸事件的分发机制

2016-12-16 10:19 169 查看

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事件进行拦截;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: