iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >详细分析Android中onTouch事件传递机制
  • 467
分享到

详细分析Android中onTouch事件传递机制

Android 2022-06-06 07:06:02 467人浏览 独家记忆
摘要

onTach介绍 ontach是Android系统中整个事件机制的基础。Android中的其他事件,如onClick、onLonGClick等都是以onTach为基础的。 on

onTach介绍

ontach是Android系统中整个事件机制的基础。Android中的其他事件,如onClick、onLonGClick等都是以onTach为基础的。

onTach包括从手指按下到离开手机屏幕的整个过程,在微观形式上,具体表现为action_down、action_move和action_up等过程。

onTach两种主要定义形式如下:

1.在自定义控件中,常见的有重写

onTouchEvent(MotionEvent ev)
方法。如在开发中经常可以看到重写的onTouchEvent方法,

并且其中有针对不同的微观表现(action_down、action_move和action_up等)做出的相应判断,执行逻辑并可能返回不同的布尔值。

2.在代码中,直接对现有控件设置setOnTouchListener监听器。并重写监听器的onTouch方法。onTouch回调函数中有view和MotionEvent

onTouch事件传递机制

大家都知道一般我们使用的UI控件都是继承自共同的父类——View。所以View这个类应该掌管着onTouch事件的相关处理。那就让我们去看看:在View中寻找Touch相关的方法,其中一个很容易地引起了我们的注意:

 dispatchTouchEvent(MotionEvent event) 

根据方法名的意思应该是负责分发触摸事件的,下面给出了源码



 public boolean dispatchTouchEvent(MotionEvent event) {
 // If the event should be handled by accessibility focus first.
 if (event.isTargetAccessibilityFocus()) {
 // We don't have focus or no virtual descendant has it, do not handle the event.
 if (!isAccessibilityFocusedViewOrHost()) {
  return false;
 }
 // We have focus and Got the event, then use nORMal event dispatch.
 event.setTargetAccessibilityFocus(false);
 }
 boolean result = false;
 if (mInputEventConsistencyVerifier != null) {
 mInputEventConsistencyVerifier.onTouchEvent(event, 0);
 }
 final int actionMasked = event.getActionMasked();
 if (actionMasked == MotionEvent.ACTION_DOWN) {
 // Defensive cleanup for new gesture
 stopNestedScroll();
 }
 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;
 }
 }
 if (!result && mInputEventConsistencyVerifier != null) {
 mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
 }
 // Clean up after nested scrolls if this is the end of a gesture;
 // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
 // of the gesture.
 if (actionMasked == MotionEvent.ACTION_UP ||
  actionMasked == MotionEvent.ACTION_CANCEL ||
  (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
 stopNestedScroll();
 }
 return result;
}

源码有点长,但我们不必每一行都看。首先注意到dispatchTouchEvent的返回值是boolean类型的,注释上的解释

:@return True if the event was handled by the view, false otherwise.
也就是说如果该触摸事件被这个View消费了就返回true,否则返回false。在方法中首先判断了该event是否是否得到了焦点,如果没有得到焦点直接返回false。然后让我们把目光转向
if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event))
这个片段,看到这里有一个名为li的局部变量,属于 ListenerInfo 类,经 mListenerInfo 赋值得到。ListenerInfo只是一个包装类,里面封装了大量的监听器。

再在 View 类中去寻找 mListenerInfo ,可以看到下面的代码:


ListenerInfo getListenerInfo() {
 if (mListenerInfo != null) {
 return mListenerInfo;
 }
 mListenerInfo = new ListenerInfo();
 return mListenerInfo;
}

因此我们可以知道mListenerInfo是不为空的,所以li也不是空,第一个判断为true,然后看到li.mOnTouchListener,前面说过ListenerInfo是一个监听器的封装类,所以我们同样去追踪mOnTouchListener:



public void setOnTouchListener(OnTouchListener l) {
 getListenerInfo().mOnTouchListener = l;
}

正是通过上面的方法来设置 mOnTouchListener 的,我想上面的方法大家肯定都很熟悉吧,正是我们平时经常用的 xxx.setOnTouchListener ,好了我们从中得知如果设置了OnTouchListener则第二个判断也为true,第三个判断为如果该View是否为enable,默认都是enable的,所以同样为true。还剩最后一个:

li.mOnTouchListener.onTouch(this, event) 
,显然是回调了第二个判断中监听器的
onTouch()
方法,如果
onTouch()
方法返回true,则上面四个判断全部为true,
dispatchTouchEvent()
方法会返回true,并且不会执行
if (!result && onTouchEvent(event))
这个判断;而在这个判断中我们又看到了一个熟悉的方法:
onTouchEvent()
。所以想要执行onTouchEvent,则在上面的四个判断中必须至少有一个false。

那就假定我们在

onTouch()
方法中返回的是false,这样就顺利地执行了onTouchEvent,那就看看onTouchEvent的源码吧:



public boolean onTouchEvent(MotionEvent event) {
 final float x = event.getX();
 final float y = event.getY();
 final int viewFlags = mViewFlags;
 final int action = event.getAction();
 if ((viewFlags & ENABLED_MASK) == DISABLED) {
 if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  setPressed(false);
 }
 // A disabled view that is clickable still consumes the touch
 // events, it just doesn't respond to them.
 return (((viewFlags & CLICKABLE) == CLICKABLE
  || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
  || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
 }
 if (mTouchDelegate != null) {
 if (mTouchDelegate.onTouchEvent(event)) {
  return true;
 }
 }
 if (((viewFlags & CLICKABLE) == CLICKABLE ||
  (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
  (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
 switch (action) {
  case MotionEvent.ACTION_UP:
  boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
  if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
   // take focus if we don't have it already and we should in
   // touch mode.
   boolean focusTaken = false;
   if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
   focusTaken = requestFocus();
   }
   if (prepressed) {
   // The button is being released before we actually
   // showed it as pressed. Make it show the pressed
   // state now (before scheduling the click) to ensure
   // the user sees it.
   setPressed(true, x, y);
   }
   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();
    }
   }
   }
   if (mUnsetPressedState == null) {
   mUnsetPressedState = new UnsetPressedState();
   }
   if (prepressed) {
   postDelayed(mUnsetPressedState,
    ViewConfiguration.getPressedStateDuration());
   } else if (!post(mUnsetPressedState)) {
   // If the post failed, unpress right now
   mUnsetPressedState.run();
   }
   removeTapCallback();
  }
  mIgnoreNextUpEvent = false;
  break;
  case MotionEvent.ACTION_DOWN:
  mHasPerformedLongPress = false;
  if (performButtonActionOnTouchDown(event)) {
   break;
  }
  // Walk up the hierarchy to determine if we're inside a scrolling container.
  boolean isInScrollingContainer = isInScrollingContainer();
  // For views inside a scrolling container, delay the pressed feedback for
  // a short period in case this is a scroll.
  if (isInScrollingContainer) {
   mPrivateFlags |= PFLAG_PREPRESSED;
   if (mPendingCheckForTap == null) {
   mPendingCheckForTap = new CheckForTap();
   }
   mPendingCheckForTap.x = event.getX();
   mPendingCheckForTap.y = event.getY();
   postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  } else {
   // Not inside a scrolling container, so show the feedback right away
   setPressed(true, x, y);
   checkForLongClick(0);
  }
  break;
  case MotionEvent.ACTION_CANCEL:
  setPressed(false);
  removeTapCallback();
  removeLongPressCallback();
  mInContextButtonPress = false;
  mHasPerformedLongPress = false;
  mIgnoreNextUpEvent = false;
  break;
  case MotionEvent.ACTION_MOVE:
  drawableHotspotChanged(x, y);
  // Be lenient about moving outside of buttons
  if (!pointInView(x, y, mTouchSlop)) {
   // Outside button
   removeTapCallback();
   if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
   // Remove any future long press/tap checks
   removeLongPressCallback();
   setPressed(false);
   }
  }
  break;
 }
 return true;
 }
 return false;
}

这段源码比 dispatchTouchEvent 的还要长,不过同样我们挑重点的看:

if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE)
看到这句话就大概知道了主要是判断该view是否是可点击的,如果可以点击则接着执行,否则直接返回false。可以看到if里面用switch来判断是哪种触摸事件,但在最后都是返回true的。

还有一点要注意:在 ACTION_UP 中会执行

performClick() 
方法:


public boolean performClick() {
 final boolean result;
 final ListenerInfo li = mListenerInfo;
 if (li != null && li.mOnClickListener != null) {
 playSoundEffect(SoundEffectConstants.CLICK);
 li.mOnClickListener.onClick(this);
 result = true;
 } else {
 result = false;
 }
 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
 return result;
}

可以看到上面的

li.mOnClickListener.onClick(this); 
,没错,我们好像又有了新的发现。根据上面的经验,这句代码会去回调我们设置好的点击事件监听器。也就是我们平常用的
xxx.setOnClickListener(listener);



public void setOnClickListener(@Nullable OnClickListener l) {
 if (!isClickable()) {
 setClickable(true);
 }
 getListenerInfo().mOnClickListener = l;
}

我们可以看到上面方法设置正是mListenerInfo的点击监听器,验证了上面的猜想。到了这里onTouch事件的传递机制基本已经分析完成了,也算是告一段落了。

好了,这下我们可以解决开头的问题了,顺便我们再来小结一下:在dispatchTouchEvent中,如果设置了OnTouchListener并且View是enable的,那么首先被执行的是OnTouchListener中的

onTouch(View v, MotionEvent event) 
。若onTouch返回true,则dispatchTouchEvent不再往下执行并且返回true;不然会执行onTouchEvent,在onTouchEvent中若View是可点击的,则返回true,不然为false。还有在onTouchEvent中若View是可点击以及当前触摸事件为ACTION_UP,会执行
performClick() 
,回调OnClickListener的onClick方法。

下面是我画的一张草图:

还有一点值得注意的地方是:假如当前事件是ACTION_DOWN,只有dispatchTouchEvent返回true了之后该View才会接收到接下来的ACTION_MOVE,ACTION_UP事件,也就是说只有事件被消费了才能接收接下来的事件。

总结

以上就是关于Android中onTouch事件传递机制的详细分析,希望对各位Android开发者们的学习或者工作能有一定的帮助,如果有疑问大家可以留言交流。

您可能感兴趣的文章:详解Android的两种事件处理机制Android View 事件分发机制详解深入解析Android中的事件传递Android事件的分发机制详解老生常谈android中的事件传递和处理机制


--结束END--

本文标题: 详细分析Android中onTouch事件传递机制

本文链接: https://www.lsjlt.com/news/23689.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android触摸事件传递机制初识
    前言今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套ListView,都是上下滑动,这该如何解决呢...
    99+
    2023-05-31
    android 触摸 roi
  • android点击事件传递机制是什么
    Android中的点击事件传递机制是基于View的层次结构进行的。当用户触摸屏幕时,Android系统会将触摸事件传递给最上层的Vi...
    99+
    2023-08-18
    android
  • android点击事件传递机制怎么实现
    Android的点击事件传递机制是通过View的dispatchTouchEvent方法和onTouchEvent方法实现的。首先,...
    99+
    2023-10-20
    android
  • Android事件分发机制 ViewGroup分析
    目录整体流程源码分析前言: 事件分发从手指触摸屏幕开始,即产生了触摸信息,被底层系统捕获后会传递给Android的输入系统服务IMS,通过Binder把消息发送到activity,a...
    99+
    2024-04-02
  • Android事件分发机制示例分析
    Android事件类型 public boolean onTouchEvent(MotionEvent event) { switch (event.getActio...
    99+
    2024-04-02
  • Android事件分发机制的详解
    Android事件分发机制我们只考虑最重要的四个触摸事件,即:DOWN,MOVE,UP和CANCEL。一个手势(gesture)是一个事件列,以一个DOWN事件开始(当用户触摸屏幕时产生),后跟0个或多个MOVE事件(当用户四处移动手指时产...
    99+
    2023-05-30
    android 事件 分发
  • js中事件传播机制的示例分析
    这篇文章主要介绍js中事件传播机制的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!事件的默认传播机制:  捕获阶段:从外向里依次查找元素  目标阶段:从当前事件源本身的操作 ...
    99+
    2024-04-02
  • Android事件分发机制全面解析
    目录事件分发机制 ViewGroup.dispatchTouchEvent 源码分析 View.dispatchTouchEvent 和 View.onTouchEvent 源码分析...
    99+
    2024-04-02
  • 聊聊Android中的事件分发机制
    目录Activity的事件分发机制 ViewGroup的事件分发机制 View的事件分发机制 View事件分发机制的本质就是就是MotionEvent事件的分发过程,即MotionE...
    99+
    2024-04-02
  • Android之事件分发机制与冲突详解
    在日常的开发过程中,我们往往会在同一个界面中出现内外两层或者多层View同时滑动的现象,这个时候往往**会出现滑动冲突。面对滑动冲突,很多开发人员不知道从哪里入手,**即便稍微有点思...
    99+
    2024-04-02
  • 在Android中使用InputManagerService进行事件传递
    在Android中使用InputManagerService进行事件传递?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。InputManagerServicepublic voi...
    99+
    2023-05-31
    android 事件传递 inputmanagerservice
  • 深入浅析Android项目中的 View事件分发机制
    本篇文章为大家展示了深入浅析Android项目中的 View事件分发机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。具体方法如下:public class MyButton extends But...
    99+
    2023-05-31
    view roi 目中
  • MySQL锁机制的详细分析
    这篇文章主要介绍MySQL锁机制的详细分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!文章目录锁锁分类共享锁(读锁,读写互斥,读读互不影响)排他锁(写锁,独占锁)共享锁和排他锁的区...
    99+
    2024-04-02
  • android事件传递与分发的流程是什么
    在Android中,事件传递与分发的流程如下:1. 事件产生:事件可以由用户触摸屏幕、按下按钮等方式产生。2. 事件分发:事件由系统...
    99+
    2023-10-18
    android
  • C++ 函数调用详解:参数传递机制深入分析
    c++++ 函数调用有三种参数传递机制:传值调用(复制参数值),传引用调用(传递参数引用,可修改原始变量),指针传递(传递参数指针)。选择机制需考虑参数大小、是否需修改原始变量和效率。 ...
    99+
    2024-05-04
    c++ 函数调用
  • Mybatis插件机制详细解析
    本篇内容介绍了“Mybatis插件机制详细解析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mybatis...
    99+
    2024-04-02
  • Spring超详细讲解事务和事务传播机制
    目录为什么需要事务Spring 声明事务Transactional参数说明propagationisolationtimeout事务回滚失效解决方案@Transactional工作原...
    99+
    2024-04-02
  • Ext.js4.2.1中事件机制的示例分析
    这篇文章主要为大家展示了“Ext.js4.2.1中事件机制的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Ext.js4.2.1中事件机制的示例分析”这篇文章吧。一: 简介Ext中的事件...
    99+
    2023-06-04
  • JavaScript中事件机制的示例分析
    这篇文章主要介绍JavaScript中事件机制的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!事件是将JavaScript脚本与网页联系在一起的主要方式,是JavaScrip...
    99+
    2024-04-02
  • 基于SSIS事件向上传递的示例分析
    这篇文章主要介绍了基于SSIS事件向上传递的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在SSIS中,Package是Task组件...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作