Android:onInterceptTouchEvent和dispatchTouchEvent之间的区别?
Android中的onInterceptTouchEvent
和dispatchTouchEvent
什么区别?
根据android开发者指南,这两种方法都可以用来拦截触摸事件( MotionEvent
),但是有什么区别?
onInterceptTouchEvent
, dispatchTouchEvent
和onTouchEvent
如何在onInterceptTouchEvent
的层次结构( ViewGroup
)中一起交互?
揭示这个秘密的最好的地方就是源代码。 这个文档对于解释这个问题是非常不充分的。
dispatchTouchEvent实际上是在Activity,View和ViewGroup上定义的。 把它看作是决定如何路由触摸事件的控制器。
例如,最简单的情况就是View.dispatchTouchEvent ,它会将触摸事件路由到OnTouchListener.onTouch(如果已定义)或扩展方法onTouchEvent 。
对于ViewGroup.dispatchTouchEvent事情更复杂。 它需要找出哪个子视图应该得到事件(通过调用child.dispatchTouchEvent)。 这基本上是一个命中testingalgorithm,您可以确定哪个子视图的边界矩形包含触点坐标。
但是在将事件分派给适当的子视图之前,父母可以一起窥探和/或拦截事件。 这是onInterceptTouchEvent的用途。 所以它在进行命中testing之前先调用这个方法,如果事件被劫持(通过从onInterceptTouchEvent返回true),它将ACTION_CANCEL发送到子视图,以便他们可以放弃他们的触摸事件处理(从先前的触摸事件)父级别的所有触摸事件都被分派到onTouchListener.onTouch (如果已定义)或onTouchEvent ()。 在这种情况下,onInterceptTouchEvent也不会再被调用。
你甚至想要重写[Activity | ViewGroup | View] .dispatchTouchEvent? 除非你正在做一些自定义路由你可能不应该。
如果要在父级别侦听和/或拦截触摸事件,并且要对主要事件处理进行View.onTouchListener / View.onTouchEvent,则主要的扩展方法是ViewGroup.onInterceptTouchEvent。
总而言之,它的devise过于复杂,但是android apis比简单更倾向于灵活性。
因为这是Google的第一个结果。 我想和你分享一下Dave Smith在Youtube上的精彩演讲:掌握Android Touch系统和幻灯片可以在这里find 。 这让我对Android Touch系统有了很深入的了解:
活动如何处理触摸:
Activity.dispatchTouchEvent()
- 总是先被调用
- 将事件发送到附加到窗口的根视图
onTouchEvent()
- 如果没有视图消耗事件,则调用
- 总是最后被调用
视图如何处理触摸:
View.dispatchTouchEvent()
- 如果存在,则首先将事件发送给侦听器
View.OnTouchListener.onTouch()
- 如果不消耗,则处理触摸本身
View.onTouchEvent()
ViewGroup如何处理触摸:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- 检查它是否应取代儿童
- 将
ACTION_CANCEL
传递给活动的孩子- 返回一次,消耗所有后续事件
- 对于每个子视图,按相反顺序添加它们
- 如果touch是相关的(在视图内),
child.dispatchTouchEvent()
- 如果以前没有处理,则发送到下一个视图
- 如果没有孩子处理事件,听众有机会
OnTouchListener.onTouch()
- 如果没有听众,或没有处理
onTouchEvent()
- 截获的事件跳过子步骤
他还在github.com/devunwired/上提供了自定义触摸的示例代码。
答:基本上,在每个View
图层上调用dispatchTouchEvent()
以确定View
是否对正在进行的手势感兴趣。 在ViewGroup
, ViewGroup
可以在dispatchTouchEvent()
方法中窃取触摸事件,然后调用子项的dispatchTouchEvent()
。 ViewGroup
只会在ViewGroup
onInterceptTouchEvent()
方法返回true时停止调度。 不同之处在于dispatchTouchEvent()
派发onInterceptTouchEvent
, onInterceptTouchEvent
告诉它是否应该截取(不派发MotionEvent
给子项)或不派发给子项 。
你可以想象一个ViewGroup的代码或多或less的这个(非常简化):
public boolean dispatchTouchEvent(MotionEvent ev) { if(!onInterceptTouchEvent()){ for(View child : children){ if(child.dispatchTouchEvent(ev)) return true; } } return super.dispatchTouchEvent(ev); }
这些方法有很多困惑,但其实并不复杂。 大部分的困惑是因为:
- 如果您的
View/ViewGroup
或其任何子项在onTouchEvent
不返回true,则onTouchEvent
dispatchTouchEvent
和onInterceptTouchEvent
。 如果没有来自onTouchEvent
的true,则父视图将假定您的视图不需要MotionEvents。 - 当ViewGroup的所有子元素都不会在onTouchEvent中返回true时,即使您的ViewGroup在
onTouchEvent
返回true,也onTouchEvent
。
处理顺序是这样的:
-
dispatchTouchEvent
被调用。 -
onInterceptTouchEvent
调用onInterceptTouchEvent
或ViewGroup的onTouchEvent
在onTouchEvent
返回true时。 - 首先在ViewGroup的子项上调用
onTouchEvent
,当没有一个子项返回true时,它将在View/ViewGroup
上调用。
如果你想预览TouchEvents/MotionEvents
而不禁用你的孩子的事件,你必须做两件事:
- 覆盖
dispatchTouchEvent
以预览事件并返回super.dispatchTouchEvent(ev)
; - 重写
onTouchEvent
并返回true,否则除onTouchEvent
,您将不会获得任何MotionEvent.ACTION_DOWN
。
如果你想检测一个像滑动事件一样的手势,只要你没有检测到手势,就不会禁用你的孩子上的其他事件,你可以这样做:
- 如上所述预览MotionEvents,并在检测到您的手势时设置一个标志。
- 当您的标志设置为取消您的孩子的
onInterceptTouchEvent
时,在onInterceptTouchEvent
返回true。 这也是重置你的标志的一个方便的地方,因为在下一个MotionEvent.ACTION_DOWN之前,onInterceptTouchEvent不会再被调用。
在FrameLayout
中覆盖的例子(我的例子是C#,因为我使用Xamarin Android进行编程,但是Java中的逻辑是一样的):
public override bool DispatchTouchEvent(MotionEvent e) { // Preview the touch event to detect a swipe: switch (e.ActionMasked) { case MotionEventActions.Down: _processingSwipe = false; _touchStartPosition = e.RawX; break; case MotionEventActions.Move: if (!_processingSwipe) { float move = e.RawX - _touchStartPosition; if (move >= _swipeSize) { _processingSwipe = true; _cancelChildren = true; ProcessSwipe(); } } break; } return base.DispatchTouchEvent(e); } public override bool OnTouchEvent(MotionEvent e) { // To make sure to receive touch events, tell parent we are handling them: return true; } public override bool OnInterceptTouchEvent(MotionEvent e) { // Cancel all children when processing a swipe: if (_cancelChildren) { // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down: _cancelChildren = false; return true; } return false; }
我在这个网页http://doandroids.com/blogs/tag/codeexample/上find了非常直观的解释。; 从那里采取:
- boolean onTouchEvent(MotionEvent ev) – 只要检测到以此View为目标的触摸事件就被调用
- boolean onInterceptTouchEvent(MotionEvent ev) – 只要检测到该ViewGroup或其子对象的触摸事件,就调用它。 如果此函数返回true,那么MotionEvent将被截取,这意味着它不会被传递给子视图,而是传递给此视图的onTouchEvent。
dispatchTouchEvent在onInterceptTouchEvent之前处理。
使用这个简单的例子:
main = new LinearLayout(this){ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { System.out.println("Event - onInterceptTouchEvent"); return super.onInterceptTouchEvent(ev); //return false; //event get propagated } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("Event - dispatchTouchEvent"); return super.dispatchTouchEvent(ev); //return false; //event DONT get propagated } }; main.setBackgroundColor(Color.GRAY); main.setLayoutParams(new LinearLayout.LayoutParams(320,480)); viewA = new EditText(this); viewA.setBackgroundColor(Color.YELLOW); viewA.setTextColor(Color.BLACK); viewA.setTextSize(16); viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80)); main.addView(viewA); setContentView(main);
你可以看到日志会像
I/System.out(25900): Event - dispatchTouchEvent I/System.out(25900): Event - onInterceptTouchEvent
因此,如果您正在使用这2个处理程序,请使用dispatchTouchEvent来处理第一个实例的事件,该事件将转至onInterceptTouchEvent。
另一个区别是,如果dispatchTouchEvent返回“false”事件不传播给子,在这种情况下EditText,而如果你在onInterceptTouchEvent返回false事件仍然得到调度到EditText
您可以在这个video中find答案https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19和接下来的3个video。; 所有的触摸事件都解释得很好,这个例子非常清楚和充满。
ViewGroup子类中的以下代码将阻止其父容器接收触摸事件:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { // Normal event dispatch to this container's children, ignore the return value super.dispatchTouchEvent(ev); // Always consume the event so it is not dispatched further up the chain return true; }
我用这个自定义覆盖,以防止背景视图响应触摸事件。
补充答案
以下是对其他答案的一些视觉补充。 我的完整答案在这里 。
dispatchTouchEvent()
方法使用onInterceptTouchEvent()
来select是否立即处理触摸事件(使用onTouchEvent()
)或继续通知其子项的dispatchTouchEvent()
方法。
主要区别:
•Activity.dispatchTouchEvent(MotionEvent) – 这允许您的Activity在发送到窗口之前拦截所有触摸事件。
•ViewGroup.onInterceptTouchEvent(MotionEvent) – 这允许ViewGroup在事件被分派到子视图时观察事件。
ViewGroup的onInterceptTouchEvent()
始终是ACTION_DOWN
事件发生的第一个事件的入口点。
如果您希望ViewGroup处理此手势,请从onInterceptTouchEvent()
返回true。 在返回true时,ViewGroup的onTouchEvent()
将接收所有后续事件,直到下一个ACTION_UP
或ACTION_CANCEL
,并且在大多数情况下, ACTION_DOWN
和ACTION_UP
或ACTION_CANCEL
之间的触摸事件是ACTION_MOVE
,通常将其识别为滚动/ ACTION_MOVE
手势。
如果从onInterceptTouchEvent()
返回false, onInterceptTouchEvent()
目标视图的onTouchEvent()
。 对于随后的消息,它将被重复,直到从onInterceptTouchEvent()
返回true。
来源: http : //neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html
Activity和View都有方法dispatchTouchEvent()和onTouchEvent。ViewGroup也有这个方法,但是有另外一个叫做onInterceptTouchEvent的方法。 这些方法的返回types是布尔型的,可以通过返回值来控制调度路由。
Android中的事件派发从Activity-> ViewGroup-> View开始。
public boolean dispatchTouchEvent(MotionEvent ev){ boolean consume =false; if(onInterceptTouchEvent(ev){ consume = onTouchEvent(ev); }else{ consume = child.dispatchTouchEvent(ev); } }
小答案:
onInterceptTouchEvent在setOnTouchListener之前。
- 如何在Android中添加填充到渐变<shape>?
- 我怎样才能得到安装Android设备的外部存储列表
- SecurityException:调用者uid XXXX与authentication者的uid不同
- 是否有可能通过我的android应用程序执行adb命令?
- 如何停止在Android Studio中滚动logcat
- Environment.getExternalStorageDirectory不会返回可移动存储的path
- GooglePlayServicesUtil与GoogleApiAvailability
- Android的AnimationDrawable和知道什么时候animation结束
- 如何在不打开Play商店的情况下以编程方式安装应用程序(就像Google Drive一样)