onInterceptTouchEvent
Android和Android有什么区别dispatchTouchEvent
?
根据android开发者指南,这两种方法都可以用来拦截一个触摸事件(MotionEvent
),但是有什么区别呢?
如何在 Views ( ) 的层次结构中进行交互onInterceptTouchEvent
和dispatchTouchEvent
交互?onTouchEvent
ViewGroup
onInterceptTouchEvent
Android和Android有什么区别dispatchTouchEvent
?
根据android开发者指南,这两种方法都可以用来拦截一个触摸事件(MotionEvent
),但是有什么区别呢?
如何在 Views ( ) 的层次结构中进行交互onInterceptTouchEvent
和dispatchTouchEvent
交互?onTouchEvent
ViewGroup
揭秘这一点的最佳地点是源代码。文档不足以解释这一点。
dispatchTouchEvent 实际上是定义在 Activity、View 和 ViewGroup 上的。将其视为决定如何路由触摸事件的控制器。
例如,最简单的情况是View.dispatchTouchEvent,它将触摸事件路由到OnTouchListener.onTouch(如果已定义)或扩展方法onTouchEvent。
对于ViewGroup.dispatchTouchEvent,事情要复杂得多。它需要弄清楚它的哪个子视图应该获取事件(通过调用 child.dispatchTouchEvent)。这基本上是一种命中测试算法,您可以在其中找出哪个子视图的边界矩形包含触摸点坐标。
但是在它可以将事件分派到适当的子视图之前,父级可以一起监视和/或拦截事件。这就是onInterceptTouchEvent的用途。因此,它在进行命中测试之前首先调用此方法,如果事件被劫持(通过从 onInterceptTouchEvent 返回 true),它会向子视图发送ACTION_CANCEL,以便他们可以放弃其触摸事件处理(来自先前的触摸事件),然后从那时起父级的所有触摸事件都被调度到onTouchListener.onTouch(如果已定义)或onTouchEvent ()。同样在这种情况下,永远不会再次调用 onInterceptTouchEvent。
你甚至想要覆盖 [Activity|ViewGroup|View].dispatchTouchEvent 吗?除非您正在执行一些自定义路由,否则您可能不应该这样做。
主要的扩展方法是 ViewGroup.onInterceptTouchEvent 如果你想在父级监视和/或拦截触摸事件,以及 View.onTouchListener/View.onTouchEvent 用于主要事件处理。
总而言之,它的设计过于复杂,但 android api 更倾向于灵活性而不是简单性。
因为这是谷歌上的第一个结果。我想在Youtube 上与您分享 Dave Smith 的精彩演讲:掌握 Android 触控系统,幻灯片可在此处获得。它让我对Android Touch System有了一个很好的深入了解:
Activity如何处理触摸:
Activity.dispatchTouchEvent()
- 总是第一个被调用
- 将事件发送到附加到 Window 的根视图
onTouchEvent()
- 如果没有视图消耗事件,则调用
- 总是最后被叫到
视图如何处理触摸:
View.dispatchTouchEvent()
- 如果存在,首先将事件发送到侦听器
View.OnTouchListener.onTouch()
- 如果未消耗,则自行处理触摸
View.onTouchEvent()
ViewGroup如何处理触摸:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- 检查它是否应该取代儿童
- 传递
ACTION_CANCEL
给活跃的孩子- 如果它返回一次 true,则
ViewGroup
消耗所有后续事件- 对于每个子视图(以相反的顺序添加它们)
- 如果触摸是相关的(内部视图),
child.dispatchTouchEvent()
- 如果它没有被前一个处理,分派到下一个视图
- 如果没有孩子处理该事件,则侦听器有机会
OnTouchListener.onTouch()
- 如果没有监听器,或者它没有被处理
onTouchEvent()
- 拦截的事件跳过子步骤
他还在github.com/devunwired/上提供了自定义触摸的示例代码。
答:
基本上dispatchTouchEvent()
每一层都会调用View
来确定 aView
是否对正在进行的手势感兴趣。在一个ViewGroup
有ViewGroup
能力在他的 - 方法中窃取触摸事件dispatchTouchEvent()
之前,它会调用dispatchTouchEvent()
孩子。仅当-method 返回 trueViewGroup
时才会停止调度。不同ViewGroup
onInterceptTouchEvent()
之处在于分派并告诉它是否应该拦截(不分派给孩子)或不应该(分派给孩子)。dispatchTouchEvent()
MotionEvents
onInterceptTouchEvent
MotionEvent
您可以想象ViewGroup 的代码或多或少地这样做(非常简化):
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
以下是其他答案的一些视觉补充。我的完整答案在这里。
a的dispatchTouchEvent()
方法ViewGroup
用于onInterceptTouchEvent()
选择是立即处理触摸事件(with onTouchEvent()
)还是继续通知dispatchTouchEvent()
其子项的方法。
这些方法有很多混淆,但实际上并没有那么复杂。大多数困惑是因为:
View/ViewGroup
或它的任何孩子没有返回 true
onTouchEvent
,dispatchTouchEvent
并且onInterceptTouchEvent
只会被调用MotionEvent.ACTION_DOWN
。如果没有真正的 from
onTouchEvent
,父视图将假定您的视图不需要 MotionEvents。MotionEvent.ACTION_DOWN
,即使您的 ViewGroup 在中返回 true onTouchEvent
。处理顺序是这样的:
dispatchTouchEvent
叫做。onInterceptTouchEvent
被调用MotionEvent.ACTION_DOWN
或当 ViewGroup 的任何子级在 中返回 true 时onTouchEvent
。onTouchEvent
首先在 ViewGroup 的子级上调用,当没有一个子级返回 true 时,它会在
View/ViewGroup
.如果您想在TouchEvents/MotionEvents
不禁用孩子事件的情况下进行预览,您必须做两件事:
dispatchTouchEvent
以预览事件并返回
super.dispatchTouchEvent(ev)
;onTouchEvent
并返回 true,否则你将不会得到任何
MotionEvent
except MotionEvent.ACTION_DOWN
。如果您想检测像滑动事件这样的手势,只要您没有检测到手势,就不会禁用您孩子的其他事件,您可以这样做:
onInterceptTouchEvent
当您的标志设置为取消您的孩子的 MotionEvent 处理时,返回 true 。这也是重置标志的方便位置,因为 onInterceptTouchEvent 直到 next 才会再次调用MotionEvent.ACTION_DOWN
。a 中的覆盖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;
}
简短的回答: 首先dispatchTouchEvent()
会被调用。
简短的建议:不应该覆盖dispatchTouchEvent()
,因为它很难控制,有时它会降低你的表现。恕我直言,我建议覆盖onInterceptTouchEvent()
.
因为大多数答案都非常清楚地提到了活动/视图组/视图上的流触摸事件,所以我只在ViewGroup
(忽略dispatchTouchEvent()
)中添加了有关这些方法的代码的更多详细信息:
onInterceptTouchEvent()
会先被调用,ACTION 事件会分别被调用 down -> move -> up。有2种情况:
如果你在 3 种情况下返回 false(ACTION_DOWN、ACTION_MOVE、ACTION_UP),它会认为父母不需要这个触摸事件,所以onTouch()
父母永远不会调用,但onTouch()
孩子会调用;但是请注意:
onInterceptTouchEvent()
仍然继续接收触摸事件,只要它的孩子不调用requestDisallowInterceptTouchEvent(true)
。onTouch()
父母。反之亦然,如果你返回 true,父母会立即窃取这个触摸事件,并onInterceptTouchEvent()
立即停止,而不是onTouch()
父母会被调用,所有onTouch()
孩子都会收到最后一个动作事件 - ACTION_CANCEL(因此,这意味着父母偷了触摸事件,以后孩子就不能处理了)。return false的流程onInterceptTouchEvent()
是正常的,但是和 return true 的情况有一点混淆,所以在这里列出:
onTouch()
父母将再次收到 ACTION_DOWN并执行以下操作(ACTION_MOVE、ACTION_UP)。onTouch()
父母将收到下一个ACTION_MOVE(不是相同的 ACTION_MOVE onInterceptTouchEvent()
)和后续动作(ACTION_MOVE,ACTION_UP)。onTouch()
父母根本不会调用,因为父母窃取触摸事件为时已晚。另一件重要的事情是事件的 ACTION_DOWNonTouch()
将确定视图是否希望从该事件接收更多操作。如果视图在 ACTION_DOWN 处返回 true onTouch()
,则表示视图愿意从该事件中接收更多操作。否则,在 ACTION_DOWN 处返回 falseonTouch()
将意味着视图不会从该事件接收任何更多操作。
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
我在这个网页http://doandroids.com/blogs/tag/codeexample/上遇到了非常直观的解释。取自那里:
- boolean onTouchEvent(MotionEvent ev) - 每当检测到以该视图为目标的触摸事件时调用
- boolean onInterceptTouchEvent(MotionEvent ev) - 每当检测到此 ViewGroup 或其子作为目标的触摸事件时调用。如果此函数返回true,则MotionEvent 将被拦截,这意味着它不会传递给child,而是传递给此View 的onTouchEvent。
The following code within a ViewGroup subclass would prevent it's parent containers from receiving touch events:
@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;
}
I used this with a custom overlay to prevent background views from responding to touch events.
您可以在此视频https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19和接下来的 3 个视频中找到答案。所有的触摸事件都解释得很好,很清楚,而且例子很多。
主要区别:
•Activity.dispatchTouchEvent(MotionEvent) - 这允许您的活动在它们被分派到窗口之前拦截所有触摸事件。
•ViewGroup.onInterceptTouchEvent(MotionEvent) - 这允许ViewGroup 在事件被分派到子视图时观察它们。
ViewGrouponInterceptTouchEvent()
始终ACTION_DOWN
是第一个发生的事件的入口点。
如果您希望 ViewGroup 处理此手势,请从onInterceptTouchEvent()
. 返回 true 后,ViewGroup将接收到 next or之前的onTouchEvent()
所有后续事件,并且在大多数情况下,and或are之间的触摸事件通常被识别为滚动/滑动手势。ACTION_UP
ACTION_CANCEL
ACTION_DOWN
ACTION_UP
ACTION_CANCEL
ACTION_MOVE
如果从 中返回 false ,则将调用onInterceptTouchEvent()
目标视图。onTouchEvent()
它将在后续消息中重复,直到您从onInterceptTouchEvent()
.
来源: http: //neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume =false;
if(onInterceptTouchEvent(ev){
consume = onTouchEvent(ev);
}else{
consume = child.dispatchTouchEvent(ev);
}
}
Activity 和 View 都有方法 dispatchTouchEvent() 和 onTouchEvent。ViewGroup 也有这个方法,但是还有另一个方法叫做 onInterceptTouchEvent。这些方法的返回类型都是布尔值,你可以通过返回值来控制调度路线。
Android 中的事件派发从 Activity->ViewGroup->View 开始。
小答案:
onInterceptTouchEvent 出现在 setOnTouchListener 之前。