*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
转载请注明出处:https://blog.youkuaiyun.com/binbinqq86/article/details/82315399
文章目录
#前言
触摸事件分发机制一直以来都是Android中比较重要的一大块,自定义view,各种复杂的自定义手势交互都与触摸事件分发机制关系密切,想要做好这些,就要对触摸事件了解透彻,并且需要不断的去实践来加深印象,否则在自己去实现的时候就会茫然不知所措,同时这个知识点也是面试必问的经典题目,所以说掌握它是必须的,今天就来详细分析一下整个触摸事件的分发流程和相关知识。(本文理论知识较多,比较枯燥,需要极大耐心观看~)
#触摸事件相关方法
触摸事件大致涉及以下几个方法:
##1、dispatchTouchEvent
/**
* 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) {
//......
}
从源码里的注解可以看到,该方法主要是用来进行事件分发和传递的,当返回true的时候代表自己去处理,把事件传递给自己,否则就传递给其他的view。该方法也是触摸事件第一个执行的方法,后续的几个是否执行都取决于它。
##2、onInterceptTouchEvent
这个方法主要是viewGroup特有的,用来做触摸事件拦截的,默认返回false:
/**
* Implement this method to intercept all touch screen motion events. This
* allows you to watch events as they are dispatched to your children, and
* take ownership of the current gesture at any point.
*
* <p>Using this function takes some care, as it has a fairly complicated
* interaction with {@link View#onTouchEvent(MotionEvent)
* View.onTouchEvent(MotionEvent)}, and using it requires implementing
* that method as well as this one in the correct way. Events will be
* received in the following order:
*
* <ol>
* <li> You will receive the down event here.
* <li> The down event will be handled either by a child of this view
* group, or given to your own onTouchEvent() method to handle; this means
* you should implement onTouchEvent() to return true, so you will
* continue to see the rest of the gesture (instead of looking for
* a parent view to handle it). Also, by returning true from
* onTouchEvent(), you will not receive any following
* events in onInterceptTouchEvent() and all touch processing must
* happen in onTouchEvent() like normal.
* <li> For as long as you return false from this function, each following
* event (up to and including the final up) will be delivered first here
* and then to the target's onTouchEvent().
* <li> If you return true from here, you will not receive any
* following events: the target view will receive the same event but
* with the action {@link MotionEvent#ACTION_CANCEL}, and all further
* events will be delivered to your onTouchEvent() method and no longer
* appear here.
* </ol>
*
* @param ev The motion event being dispatched down the hierarchy.
* @return Return true to steal motion events from the children and have
* them dispatched to this ViewGroup through onTouchEvent().
* The current target will receive an ACTION_CANCEL event, and no further
* messages will be delivered here.
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
可以看到注释是很长的,也是很详细的,我这里大致翻译一下里面几个重要的点:
- 主要是用来做事件分发过程中的拦截的,相当于一个拦截器
- 如果返回false或者super,则事件继续传递,事件所经过的每一层的viewGroup都会去调用该方法来询问是否拦截
- 如果返回true,则代表拦截该事件,停止传递给子view,会走自己的onTouchEvent事件
- 不像onTouchEvent是否拦截取决于down事件,该方法每个事件都可以去做拦截
- 事件一经拦截,后续move、up事件都直接交给onTouchEvent,不会重新去询问是否拦截(即不再调用onInterceptTouchEvent)
- 事件被拦截后,子view会接收到一个cancel事件,来恢复之前的状态,结束当前事件流
##3、requestDisallowInterceptTouchEvent
/**
* Called when a child does not want this parent and its ancestors to
* intercept touch events with
* {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
*
* <p>This parent should pass this call onto its parents. This parent must obey
* this request for the duration of the touch (that is, only clear the flag
* after this parent has received an up or a cancel.</p>
*
* @param disallowIntercept True if the child does not want the parent to
* intercept touch events.
*/
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
这个方法也是用来做事件拦截的,也是viewGroup专有的方法,不过一般是在子view中来调用的,根据注释:当一个子view不希望它的父view来通过onInterceptTouchEvent方法拦截事件的时候,调用该方法即可实现事件的传递和接管,并且在整个事件流中,父view以及再往上的父view都要遵守该规则。
##4、onTouch
/**
* Called when a touch event is dispatched to a view. This allows listeners to
* get a chance to respond before the target view.
*
* @param v The view the touch event has been dispatched to.
* @param event The MotionEvent object containing full information about
* the event.
* @return True if the listener has consumed the event, false otherwise.
*/
boolean onTouch(View v, MotionEvent event);
这个也是触摸事件,方便给开发者调用的。根据注释:当一个触摸事件被分发到一个view的时候,就会调用该方法,它是在事件传递到onTouchEvent之前被调用的。
##5、onTouchEvent
/**
* Implement this method to handle touch screen motion events.
* <p>
* If this