*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
转载请注明出处:https://blog.csdn.net/binbinqq86/article/details/82315399
触摸事件分发机制一直以来都是Android中比较重要的一大块,自定义view,各种复杂的自定义手势交互都与触摸事件分发机制关系密切,想要做好这些,就要对触摸事件了解透彻,并且需要不断的去实践来加深印象,否则在自己去实现的时候就会茫然不知所措,同时这个知识点也是面试必问的经典题目,所以说掌握它是必须的,今天就来详细分析一下整个触摸事件的分发流程和相关知识。(本文理论知识较多,比较枯燥,需要极大耐心观看~)
触摸事件大致涉及以下几个方法:
/**
* 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。该方法也是触摸事件第一个执行的方法,后续的几个是否执行都取决于它。
这个方法主要是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.
*
* 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:
*
*
* - You will receive the down event here.
*
- 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.
*
- 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().
*
- 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.
*
*
* @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;
}
可以看到注释是很长的,也是很详细的,我这里大致翻译一下里面几个重要的点:
/**
* Called when a child does not want this parent and its ancestors to
* intercept touch events with
* {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
*
* 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.
*
* @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都要遵守该规则。
/**
* 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之前被调用的。
/**
* Implement this method to handle touch screen motion events.
*
* If this method is used to detect click actions, it is recommended that
* the actions be performed by implementing and calling
* {@link #performClick()}. This will ensure consistent system behavior,
* including:
*
* - obeying click sound preferences
*
- dispatching OnClickListener calls
*
- handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when
* accessibility features are enabled
*
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
public boolean onTouchEvent(MotionEvent event) {
//......
}
该方法就是真正用来处理触摸事件的最后调用的方法,在这里你可以自己写你的触摸事件算法。
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
这个就是我们最熟悉的点击事件了,它也属于触摸事件的一个内容,有一点需要注意就是他是在onTouchEvent的UP事件里面执行的,这就是我们下面要讲的onTouch、onClick、onTouchEvent执行顺序。
执行顺序:onTouch—>onTouchEvent—>onClick,注意:onClick直接消费掉了事件,不会再向上回溯事件了。下面我们就通过例子来验证,我们给view设置监听事件:
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick: ======" );
}
});
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e(TAG, "onTouch: ======" );
return false;
}
});
并重写onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent: ======ACTION_DOWN" );
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent: ======ACTION_MOVE" );
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent: ======ACTION_UP" );
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "onTouchEvent: ======ACTION_CANCEL" );
break;
default:
Log.e(TAG, "onTouchEvent: ======default" );
break;
}
return super.onTouchEvent(event);
}
打印日志如下:
E/V: dispatchTouchEvent: ======ACTION_UP
onTouch: ======
onTouchEvent: ======ACTION_UP
onClick: ======
证明了我们的顺序,下面把onTouch返回true:
E/V: dispatchTouchEvent: ======ACTION_UP
onTouch: ======
说明onTouch优先消费掉了事件,如果把onTouchEvent返回true呢:
E/V: dispatchTouchEvent: ======ACTION_UP
onTouch: ======
onTouchEvent: ======ACTION_UP
onClick就不执行了,说明在onTouchEvent的UP事件中,事件已经被消费了,所以最终的click事件没有被执行,下面看看源码:
public boolean dispatchTouchEvent(MotionEvent event) {
//......
boolean result = false;
//......
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;
}
}
//......
return result;
}
这里可以很明显看出,onTouch优先于onTouchEvent执行。
public boolean onTouchEvent(MotionEvent event) {
//......
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//......
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
//......
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
//......
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
//......
break;
case MotionEvent.ACTION_CANCEL:
//......
break;
case MotionEvent.ACTION_MOVE:
//......
break;
}
return true;
}
return false;
}
performClick里面就是执行的onClick事件,可以看到onClick是在onTouchEvent的UP事件里面才执行的,所以如果onTouchEvent返回了true,那么事件就被消费掉了,这段代码就不会执行,所以onClick事件就不会发生了,到此这个顺序关系应该是比较明确了。
下面我们就来看一下整个触摸事件的流转机制,首先上图:
是不是被吓一跳?哈哈,下面我们就通过详细分析来解析上图。
上图一共分为三层,Activity——>ViewGroup——>View,其中ViewGroup比其他两个多了一个onInterceptTouchEvent方法,箭头代表了事件流的走向,箭头上的值则代表了该方法的返回值,绿色的消费框则代表了事件被消费掉,就此完结,不会再往下传递或者回溯。
仔细看图,我们可以总结出以下几点:
1. 如果事件不被中断,则整个事件流就是一个完整的U型图
2.dispatchTouchEvent和onTouchEvent一旦返回true,事件就被消费掉了,该事件就消失了,不会往下传递也不会向上回溯
3.dispatchTouchEvent和onTouchEvent一旦返回false,事件就会回溯到父控件的onTouchEvent,说明自己不处理
4.所有方法的super就是默认返回值,就是保证让整个事件流按照U型图走完
5.onInterceptTouchEvent默认返回super,通过源码我们知道其实就是返回false,默认是不去拦截事件的,这也符合常理,可以让子view有机会去捕获事件,返回true则代表拦截了这个事件,交给自己的onTouchEvent去处理,ViewGroup的dispatchTouchEvent的super默认实现就是调用自己的onInterceptTouchEvent,这也就可以保证事件有机会分发到自己的onTouchEvent
6.dispatchTouchEvent和onTouchEvent都是以Down事件为基准,来判断后续事件是否经过自己,也就是自己消费,如果Down事件返回了false或者super,则后续事件都不再经过自己了,包括move,up,如只有返回true的时候,后续事件才会经过自己
下面我们通过demo来说明上图逻辑,我们首先新建一个ViewGroup,再新建一个View,还有一个Activity,分别重写他们的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,源码就不在展示了,比较简单,最终的效果图如下:
先全部按照默认的super去返回,点击中间的view看一下日志(VG代表ViewGroup,V代表View):
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====onTouchEvent=====ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
=====onTouchEvent=====ACTION_UP
看日志可以分析出跟我们上图的U型路线是一致的,由于事件一路传递下来,又一路回溯回去,所以最终事件就被Activity消费了(后续的UP事件)。注意,我们这里的返回值都是在方法的最后一行,而不是DOWN事件内,事件内产生的日志又会不一样,这个后面再说。
下面我们在View的onTouchEvent返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onInterceptTouchEvent: ======ACTION_UP
E/V: dispatchTouchEvent: ======ACTION_UP
E/V: onTouchEvent: ======ACTION_UP
我们在View的dispatchTouchEvent返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onInterceptTouchEvent: ======ACTION_UP
E/V: dispatchTouchEvent: ======ACTION_UP
我们在ViewGroup的onTouchEvent返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
我们在ViewGroup的onInterceptTouchEvent返回true,并且onTouchEvent也返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
E/VG: onInterceptTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onTouchEvent: ======ACTION_MOVE
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onTouchEvent: ======ACTION_MOVE
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
可以看到被拦截之后,后续move,up事件都交给自己处理,并且不再调用onIntercepetTouchEvent,而且事件也不再传递到子View。
下面再把ViewGroup的dispatchTouchEvent返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
Activity的dispatchTouchEvent返回true或者false:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
通过以上日志,认真的对着上图分析一遍,可以发现流程完全一致,整个的流程按照图纸来记忆还是比较容易的。
上面说到dispatchTouchEvent和onTouchEvent对事件是否消费是在DOWN事件中来决定的,如果DOWN没有返回true,则后续的move、up都不会再来找上门了,下面我们就在down事件返回true来看一下:
view的onTouchEvent中DOWN事件返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onInterceptTouchEvent: ======ACTION_UP
E/V: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
E/MainActivity: =====onTouchEvent=====ACTION_UP
可以看到比上面直接在方法最后返回true多出了最后一行日志,其他完全一致,这是因为只有down返回了true,仅仅是让后续事件经过自己,但是move、up事件返回的还是super,而ViewGroup的onTouchEvent事件已经被跳过,所以up事件回溯到Activity了。而在move和up事件中返回true则没有down返回true的作用。下面我们在down返回false看一下:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
E/VG: onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====onTouchEvent=====ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
=====onTouchEvent=====ACTION_UP
可以看到,后续move,up事件都不在经过自己了,所以说down事件起到了决定事件流向的作用。
我们在view的dispatchTouchEvent的DOWN返回true看一下:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onInterceptTouchEvent: ======ACTION_UP
E/V: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
E/MainActivity: =====onTouchEvent=====ACTION_UP
可以看到,事件走到View的dispatchTouchEvent后就停止了,因为这里返回了true,代表事件在这里消费了,而后续的UP事件同样也是走到view的dispatchTouchEvent,由于up返回的是super,所以走了自身的onTouchEvent的up,然后这里返回的也是super,所以又回溯给Activity的onTouchEvent的up了。
如果这里返回的false呢:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====onTouchEvent=====ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
=====onTouchEvent=====ACTION_UP
可以看到事件走到view的dispatchTouchEvent后回溯给父控件的onTouchEvent了,然后又继续回溯,而后续的UP事件也不会经过了。下面再来看看ViewGroup的onTouchEvent的DOWN返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
E/VG: onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
E/VG: onTouchEvent: ======ACTION_UP
E/MainActivity: =====onTouchEvent=====ACTION_UP
可以看到事件在ViewGroup的OnTouchEvent结束了,没有继续回溯,而up事件也经过了自己,由于返回了super,所以回溯到Activity。
onInterceptTouchEvent放到下面一小节,我们继续把ViewGroup的dispatchTouchEvent的DOWN返回true:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
E/MainActivity: =====onTouchEvent=====ACTION_UP
可以看到事件在dispatchTouchEvent中消费了,没有传递下去,后续的up也经过了自己。
综上可以分析到:收到了ACTION_DOWN,就会收到 ACTION_MOVE、ACTION_UP 等后续的事件的前提条件就是必须消费该事件,也就是返回true。如果仅仅是在ACTION_DOWN返回true,其他事件返回super,则其他事件没被消费,会继续向上回溯,但是一定会经过消费控件本身。
上面说了onInterceptTouchEvent不会像onTouchEvent一样必须在DOWN里面决定是否消费,它是用来做拦截的,也就相当于一个分流器,所以在它的所有事件都可以去分流,比如我们手指按下列表中的一个view,然后过一会去滑动,这个时候依然会走onInterceptTouchEvent方法,不过走的就是move了(前提是你在DOWN事件中没有去拦截,也就是让事件向子view传递),这个时候你就可以在move事件中去做拦截,来进行列表的一个滑动,这种设计是符合正常逻辑的,下面我们看一下日志(在down中返回true):
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====onTouchEvent=====ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/MainActivity: =====onTouchEvent=====ACTION_UP
可以看到直接被拦下了,走了自己的onTouchEvent,由于返回了super,所以事件回溯给Activity
再看在move中返回true(onTouchEvent中返回true,因为DOWN事件代表拦截,如果不返回true,后续move就不会再经过ViewGroup):
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/VG: onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onTouchEvent: ======ACTION_MOVE
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onTouchEvent: ======ACTION_MOVE
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
onTouchEvent: ======ACTION_UP
可以看到成功的拦下了move事件。
在上述onInterceptTouchEvent场景中,其实还少了一步,就是View的onTouchEvent的也要返回true,这样就代表一开始是点击,然后停顿一下,手指去滑动,变成滑动了,通过onInterceptTouchEvent去拦截,这个时候View就会收到一个ACTION_CANCEL事件来恢复自己初始按下的状态,看下日志:
E/MainActivity: =====dispatchTouchEvent=====ACTION_DOWN
E/VG: dispatchTouchEvent: ======ACTION_DOWN
onInterceptTouchEvent: ======ACTION_DOWN
E/V: dispatchTouchEvent: ======ACTION_DOWN
onTouchEvent: ======ACTION_DOWN
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onInterceptTouchEvent: ======ACTION_MOVE
E/V: dispatchTouchEvent: ======ACTION_CANCEL
onTouchEvent: ======ACTION_CANCEL
E/MainActivity: =====dispatchTouchEvent=====ACTION_MOVE
E/VG: dispatchTouchEvent: ======ACTION_MOVE
onTouchEvent: ======ACTION_MOVE
E/MainActivity: =====dispatchTouchEvent=====ACTION_UP
E/VG: dispatchTouchEvent: ======ACTION_UP
E/VG: onTouchEvent: ======ACTION_UP
我们成功的模拟了上述场景,事件传递到view的onTouchEvent,就在被系统认为消费的时候,然后手指滑动,进而ViewGroup去拦截该滑动,这个时候就会额外触发一个ACTION_CANCEL来传递给子view进而恢复子view的状态,设计可以说是非常巧妙,哈哈~
好了,终于讲完了,说了这么多不知道大家有没有看懂呢,还有不明白的童鞋可以给我留言,最后放出demo源码:
源码下载
参考文章: