Ann*_*oid 239 java android event-listener event-handling android-touch-event
Android onInterceptTouchEvent和dispatchTouchEventAndroid有什么区别?
根据android开发者指南,这两种方法都可以用来拦截触摸事件(MotionEvent),但有什么区别?
怎么办onInterceptTouchEvent,dispatchTouchEvent和onTouchEvent视图的层次结构中一起互动(ViewGroup)?
num*_*ati 267
揭开这个神秘面纱的最佳地方是源代码.对于解释这个问题,文档非常不足够.
dispatchTouchEvent实际上是在Activity,View和ViewGroup上定义的.可以将其视为控制器,决定如何路由触摸事件.
例如,最简单的情况是View.dispatchTouchEvent,它将触摸事件路由到OnTouchListener.onTouch(如果已定义)或扩展方法onTouchEvent.
对于ViewGroup.dispatchTouchEvent来说,事情要复杂得多.它需要确定哪个子视图应该获取事件(通过调用child.dispatchTouchEvent).这基本上是一种命中测试算法,您可以在其中确定哪个子视图的边界矩形包含触摸点坐标.
但是在它将事件分派到适当的子视图之前,父母可以一起监视和/或拦截事件.这就是onInterceptTouchEvent的用途.所以它做的命中测试之前,首先要调用此方法,如果事件被劫持(通过返回从onInterceptTouchEvent真)发送一个ACTION_CANCEL孩子的意见,使他们能够放弃自己的触摸事件处理(从以前的触摸事件),并从那时起父级别的所有触摸事件都将分派到onTouchListener.onTouch(如果已定义)或onTouchEvent().同样在这种情况下,永远不会再调用onInterceptTouchEvent.
你是否想要覆盖[Activity | ViewGroup | View] .dispatchTouchEvent?除非你做一些自定义路由,否则你可能不应该这样做.
如果你想刺探和/或在主事件处理父级别和View.onTouchListener/View.onTouchEvent拦截触摸事件的主要扩展方法是ViewGroup.onInterceptTouchEvent.
总而言之,它的设计过于复杂,但是Android apis更倾向于灵活性而不是简单性.
seb*_*seb 234
因为这是谷歌的第一个结果.我想和你分享Dave Smith在Youtube上的精彩演讲:掌握Android Touch系统,这里有幻灯片.它让我对Android Touch系统有了深刻的理解:
活动如何处理触摸:
Activity.dispatchTouchEvent()
- 总是第一次被召唤
- 将事件发送到附加到Window的根视图
onTouchEvent()
- 如果没有视图消耗该事件则调用
- 永远被称为
视图如何处理触摸:
View.dispatchTouchEvent()
- 如果存在,则首先将事件发送给侦听器
View.OnTouchListener.onTouch()- 如果没有消耗,则处理触摸本身
View.onTouchEvent()
ViewGroup如何处理触摸:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- 检查它是否应该取代孩子
- 通过
ACTION_CANCEL活跃的孩子- 返回true一次,消耗所有后续事件
- 对于每个子视图,以相反的顺序添加它们
- 如果触摸相关(内部视图),
ViewGroup- 如果以前没有处理,则调度到下一个视图
- 如果没有孩子处理事件,听众就有机会
child.dispatchTouchEvent()- 如果没有听众,或者没有处理
OnTouchListener.onTouch()- 截获的事件跳过子步
他还在github.com/devunwired/上提供了自定义触摸的示例代码.
答:
基本上onTouchEvent()是在每dispatchTouchEvent()一层上调用以确定a View是否对正在进行的手势感兴趣.在View该ViewGroup有偷他的触摸事件的能力ViewGroup-方法,之前它会叫dispatchTouchEvent()上孩子.该dispatchTouchEvent()如果只会停止调度ViewGroup ViewGroup-方法返回true.的区别是,onInterceptTouchEvent()被调度dispatchTouchEvent()和MotionEvents告诉它是否应拦截(不分派onInterceptTouchEvent到子女)或没有(派遣儿童).
您可以想象ViewGroup的代码执行或多或少(非常简化):
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*l W 19
关于这些方法存在很多困惑,但实际上并没有那么复杂.大多数混乱是因为:
View/ViewGroup或您的任何一个孩子没有返回真实
onTouchEvent,dispatchTouchEvent并且onInterceptTouchEvent只会被要求MotionEvent.ACTION_DOWN.如果没有true
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 除外MotionEvent.ACTION_DOWN.如果您想要检测某些手势,例如滑动事件,只要您没有检测到手势,就可以在子项上禁用其他事件,您可以这样做:
onInterceptTouchEvent当您的标志设置为取消您孩子的MotionEvent处理时返回true .这也是重置标志的一个方便的地方,因为在下一次之前不会再次调用onInterceptTouchEvent 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;
}
Run Code Online (Sandbox Code Playgroud)
简短回答: 首先dispatchTouchEvent()会被调用。
简短建议:不应覆盖,dispatchTouchEvent()因为它很难控制,有时会降低您的性能。恕我直言,我建议覆盖onInterceptTouchEvent().
因为大多数答案都非常清楚地提到了活动/视图组/视图上的流触摸事件,我只在ViewGroup(忽略dispatchTouchEvent())中添加有关这些方法的代码的更多详细信息:
onInterceptTouchEvent()将首先被调用,ACTION 事件将分别被调用 down -> move -> up。有2种情况:
如果在 3 种情况下(ACTION_DOWN、ACTION_MOVE、ACTION_UP)返回 false,它将认为父级不需要此触摸事件,因此onTouch()父级从不调用,但onTouch()子级会调用;但是请注意:
onInterceptTouchEvent()仍然继续接收触摸事件,只要它的孩子不叫requestDisallowInterceptTouchEvent(true)。onTouch()父母。反之亦然,如果你返回 true,父母会立即窃取这个触摸事件,并onInterceptTouchEvent()立即停止,而不是onTouch()父母会被调用,所有onTouch()孩子都会收到最后一个动作事件 - ACTION_CANCEL(因此,这意味着父母偷了触摸事件,从那时起孩子们就无法处理了)。onInterceptTouchEvent()return false的流程是正常的,但是 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()将意味着该视图不会从该事件接收任何更多操作。
我在这个网页http://doandroids.com/blogs/tag/codeexample/上得到了非常直观的解释.从那里采取:
- boolean onTouchEvent(MotionEvent ev) - 每当检测到以此View为目标的触摸事件时调用
- boolean onInterceptTouchEvent(MotionEvent ev) - 每当使用此ViewGroup或其子节点作为目标检测到触摸事件时调用.如果此函数返回true,则将拦截MotionEvent,这意味着它不会传递给子节点,而是传递给此View的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);
Run Code Online (Sandbox Code Playgroud)
您可以看到日志将如下所示:
I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent
Run Code Online (Sandbox Code Playgroud)
因此,如果您正在使用这两个处理程序,请使用dispatchTouchEvent在第一个实例上处理事件,该事件将转到onInterceptTouchEvent.
另一个区别是,如果dispatchTouchEvent返回'false',则事件不会传播给子节点,在本例中为EditText,而如果在onInterceptTouchEvent中返回false,则事件仍然会传递给EditText
主要区别:
\n\n\n\n\xe2\x80\xa2Activity.dispatchTouchEvent(MotionEvent) - 这允许您的 Activity\n 在将所有触摸事件分派到窗口之前拦截它们。
\n
\n \xe2\x80\xa2ViewGroup.onInterceptTouchEvent(MotionEvent) - 这允许 ViewGroup 在事件分派到子视图时监视事件。