本文主要是介绍Android触摸事件处理机制之requestDisallowInterceptTouchEvent,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、触摸事件传递的规则
当手指触摸到屏幕时,系统就会调用相应的View的onTouchEvent,并传入一系列的action。当有多个层级的View时,在父层级允许的情况下,这个action会一直向下传递直到遇到最深层的View。所以touch事件最先调用的是最底层View的onTouchEvent,如果View的onTouchEvent接收到某个touchaction并做了相应处理,最后有两种返回方式: retrun true 和 returnfalse;return true会告诉系统当前的View需要处理这次touch事件,以后系统发出的ACTION_MOVE,ACTION_UP等还是需要继续监听并接收的,而且这次的action已经被处理掉了,父层的View是不可能触发onTouchEvent了,所以每一个action最多只能有一个onTouchEvent接口返回true。如果返回false,便会通知系统,当前View不关心这一次的touch事件,此时这个action会传向父级,调用父级View的onTouchEvent,但是这一次的touch事件之后发出的任何action,该View都不会再接受,onTouchEvent在这一次的touch事件中再也不会触发,也就是说一旦View返回false,那么之后的ACTION_MOVE,ACTION_UP等action就不会再传入这个view,但是下一次touch事件的action还是会传进来的。
前面说到了底层的View能够接受到这次事件有一个前提:在父层级允许的情况下。假设不改变父层级的dispatch方法,在系统调用底层onTouchEvent之前会先调用父View的方法判断,父层View是不是要截获本次touch事件之后的action。如果onInterceptTouchEvent返回了true,那么本次touch事件之后的所有action都不会再向深层次的View传递,统统都会传给父层View的onTouchEvent,就是说父层已经截获了这次touch事件,之后的action也不必询问onInterceptTouchEvent,在这次的touch事件之后发出的action时,onInterceptTouchEvent不会再调用,直到下一次touch事件的来临。如果onInterceptTouchEvent返回false,那么本次action将发送给更深层的View,并且之后的每一次action都会询问父层的onInterceptTouchEvent需不需要截获本次touch事件。只有ViewGroup才有onInterceptTouchEvent方法,因为一个普通的View肯定是位于最深层的View,touch事件能够传到这里已经是最后一站了,肯定会调用View的onTouchEvent。
对于底层的View来说,有一种方法可以阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true);方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action。
二、事例分析
用例子总结一下onInterceptTouchEvent 和 onTouchEvent的调用顺序。 假设最高层View叫OuterLayout, 中间层View叫InnerLayout, 最底层View叫MyView
调用的顺序是这样的(假设各个函数返回的都是false)OuterLayout.onInterceptTouchEvent -> InnerLayout.onInterceptTouchEvent-> MyView.onTouchEvent -> InnerLayout.onTouchEvent ->OuterLayout.onTouchEvent。
public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(ev);
}
这句话是告诉父View,我的事件自己处理
public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: pager.requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: pager.requestDisallowInterceptTouchEvent(false); break; }
}
也可以写成类似于上面那样,当用户按下的时候,我们告诉父组件,不要拦截我的事件(这个时候子组件是可以正常响应事件的),拿起之后就会告诉父组件可以阻止。
三、一个关于子控件和父控件的事件响应问题
当父控件中有子控件的时候,并且父控件和子控件都有事件处理(比如单击事件)。这时,点击子控件,父控件的单击事件就无效了。
比如:一个LinearLayout里面有一个子控件TextView,但是TextView的大小没有LinearLayout大
1.如果LinearLayout和TextView都设置了单击事件,那么:
- 点击TextView区域的时候,触发的是TextView的事件。
- 点击TextView以外的区域的时候,触发LinearLayout的事件。
2.如果LinearLayout设置了单击事件,而TextView没有设置单机事件的话,那么
- 不管单击的是TextView区域,还是TextView以外的区域,都是触发LinearLayout的单击事件
如果两个控件一样大:
1.如果LinearLayout和TextView都设置了单击事件,那么:
- 只有TextView的单击事件有效
2.如果LinearLayout设置了单击事件,而TextView没有设置单机事件的话,那么
- 触发LinearLayout的单击事件
public boolean onInterceptTouchEvent (MotionEvent ev)
Implement this methodto intercept all touch screen motion events. This allows you to watch events asthey are dispatched to your children, and take ownership of the current gestureat any point.
Using this functiontakes some care, as it has a fairly complicated interaction withView.onTouchEvent(MotionEvent),and using it requires implementing that method as well as this one in thecorrect 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 actionACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
Parameters
ev | The motion event being dispatched down the hierarchy. |
Returns
- 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.
这篇关于Android触摸事件处理机制之requestDisallowInterceptTouchEvent的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!