针对屏幕上的一个View控件,Android如何区分应当触发onTouchEvent,还是onClick,亦或是onLongClick事件?

在Android中,一次用户操作可以被不同的View按次序分别处理,并将完全响应了用户一次UI操作称之为消费了该事件(consume),那么Android是按什么次序将事件传递的呢?又在什么情况下判定为消费了该事件?

搞清楚这些问题对于编写出能正确响应UI操作的代码是很重要的,尤其当屏幕上的不同View需要针对此次UI操作做出各种不同响应的时候更是如此,一个 典型例子就是用户在桌面上放置了一个Widget,那么当用户针对widget做各种操作时,桌面本身有的时候要对用户的操作做出响应,有时忽略。只有搞 清楚事件触发和传递的机制才有可能保证在界面布局非常复杂的情况下,UI控件仍然能正确响应用户操作。

1.  onTouchEvent

onTouchEvent中要处理的最常用的3个事件就是:ACTION_DOWN、ACTION_MOVE、ACTION_UP。

这三个事件标识出了最基本的用户触摸屏幕的操作,含义也 很清楚。虽然大家天天都在用它们,但是有一点请留意,ACTION_DOWN事件作为起始事件,它的重要性是要超过ACTION_MOVE和 ACTION_UP的,如果发生了ACTION_MOVE或者ACTION_UP,那么一定曾经发生了ACTION_DOWN。

从Android的源代码中能看到基于这种不同重要性的理解而实现的一些交互机制,SDK中也有明确的提及,例如在ViewGroup的 onInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后续的事件将直接发给 onTouchEvent,而不是继续发给onInterceptTouchEvent。

2.  onClick、onLongClick与onTouchEvent

曾经看过一篇帖子提到,如果在View中处理了onTouchEvent,那么就不用再处理onClick了,因为Android只会触发其中一个方 法。这个理解是不太正确的,针对某个view,用户完成了一次触碰操作,显然从传感器上得到的信号是手指按下和抬起两个操作,我们可以理解为一次 Click,也可以理解为发生了一次ACTION_DOWN和ACTION_UP,那么Android是如何理解和处理的呢?

在Android中,onClick、onLongClick的触发是和ACTION_DOWN及ACTION_UP相关的,在时序上,如果我们在一 个View中同时覆写了onClick、onLongClick及onTouchEvent的话,onTouchEvent是最先捕捉到 ACTION_DOWN和ACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的 onTouchEvent方法中实现的:

case MotionEvent.ACTION_DOWN:

mPrivateFlags |= PRESSED;

refreshDrawableState();

if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {

postCheckForLongClick();

break;

case MotionEvent.ACTION_UP:

if ((mPrivateFlags & PRESSED) != 0) {

boolean focusTaken = false;

if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {

focusTaken = requestFocus();

if (!mHasPerformedLongPress) {

if (mPendingCheckForLongPress != null) {

removeCallbacks(mPendingCheckForLongPress);

if (!focusTaken) {

performClick();

break;

可以看到,Click的触发是在系统捕捉到ACTION_UP后发生并由performClick()执行的,performClick里会调用先前注册的监听器的onClick()方法:

public boolean performClick() {

if (mOnClickListener != null) {

playSoundEffect(SoundEffectConstants.CLICK);

mOnClickListener.onClick(this);

return true;

return false;

LongClick的触发则是从ACTION_DOWN开始,由postCheckForLongClick()方法完成:

private void postCheckForLongClick() {

mHasPerformedLongPress = false;

if (mPendingCheckForLongPress == null) {

mPendingCheckForLongPress = new CheckForLongPress();

mPendingCheckForLongPress.rememberWindowAttachCount();

postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());

可以看到,在ACTION_DOWN事件被捕捉后,系统会开始触发一个postDelayed操作,delay的时间在Eclair2.1上为500ms,500ms后会触发CheckForLongPress线程的执行:

class CheckForLongPress implements Runnable {

public void run() {

if (isPressed() && (mParent != null)

&& mOriginalWindowAttachCount == mWindowAttachCount) {

if (performLongClick()) {

mHasPerformedLongPress = true;

如果各种条件都满足,那么在CheckForLongPress中执行performLongClick(),在这个方法中将调用onLongClick():

public boolean performLongClick() {

if (mOnLongClickListener != null) {

handled = mOnLongClickListener.onLongClick(View.this);

从实现中可以看到onClick()和onLongClick()方法是由ACTION_DOWN和ACTION_UP事件捕捉后根据各种情况最终确定是 否触发的,也就是说如果我们在一个Activity或者View中同时监听或者覆写了onClick(),onLongClick()和 onTouchEvent()方法,并不意味着只会发生其中一种。

下面是一个onClick被触发的基本时序的Log:

04-05 05:57:47.123: DEBUG/TSActivity(209): onTouch ACTION_DOWN

04-05 05:57:47.263: DEBUG/TSActivity(209): onTouch ACTION_UP

04-05 05:57:47.323: DEBUG/TSActivity(209): onClick

可以看出是按ACTION_DOWN -> ACTION_UP -> onClick的次序发生的。

下面是一个onLongClick被触发的基本时序的Log:

04-05 06:00:04.133: DEBUG/TSActivity(248): onTouch ACTION_DOWN

04-05 06:00:04.642: DEBUG/TSActivity(248): onLongClick

04-05 06:00:05.083: DEBUG/TSActivity(248): onTouch ACTION_UP

可以看到,在保持按下的状态一定时间后会触发onLongClick,之后抬起手才会发生ACTION_UP。

3.  onClick和onLongClick能同时发生吗?

要弄清楚这个问题只要理解Android对事件处理的所谓消费(consume)概念即可,一个用户的操作会被传递到不同的View控件和同一个控件 的不同监听方法处理,任何一个接收并处理了该次事件的方法如果在处理完后返回了true,那么该次event就算被完全处理了,其他的View或者监听方 法就不会再有机会处理该event了。

onLongClick的发生是由单独的线程完成的,并且在ACTION_UP之前,而onClick的发生是在ACTION_UP后,因此同一次用 户touch操作就有可能既发生onLongClick又发生onClick。这样是不是不可思议?所以及时向系统表示“我已经完全处理(消费)了用户的 此次操作”,是很重要的事情。例如,我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了。

下面的Log是在onLongClick()方法return false的情况下,一次触碰操作的基本时序:

04-05 06:00:53.023: DEBUG/TSActivity(277): onTouch ACTION_DOWN

04-05 06:00:53.533: DEBUG/TSActivity(277): onLongClick

04-05 06:00:55.603: DEBUG/TSActivity(277): onTouch ACTION_UP

04-05 06:00:55.663: DEBUG/TSActivity(277): onClick

可以看到,在ACTION_UP后仍然触发了onClick()方法。

Android onTouchEvent, onClick及onLongClick的调用机制的更多相关文章

  1. android中的事件传递和处理机制

    一直以来,都被android中的事件传递和处理机制深深的困扰!今天特意来好好的探讨一下.现在的感觉是,只要你理解到位,其实事件的 传递和处理机制并没有想象中的那么难.总之,不要自己打击自己,要相信自己 ...

  2. 从Android源码的角度分析Binder机制

    欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 前言 大家好,好久不见,距离上篇文章已经有35天之久了,因为身体不舒服 ...

  3. Android为TV端助力 双缓存机制

    废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...

  4. Android onTouchEvent和setOnTouchListener中onTouch的区别

    OnTouchEvent()方法 是获取的对屏幕的各种操作,比如向左向右滑动,点击返回按钮等等. 属于一个宏观的屏幕触摸监控. OnTouchListener()方法 是获取某一个控件某一个View的 ...

  5. 有关ViewPager的使用及解决Android下ViewPager和PagerAdapter中调用notifyDataSetChanged失效的问题

    ViewPager是android-support-v4.jar包中的一个系统控件,继承自ViewGroup,专门用以实现左右滑动切换View的效果,使用时需要首先在Project->prope ...

  6. Android正在使用Handler实现消息分发机制(零)

    演讲前,AsyncTask文章.我们在最后谈到.AsyncTask它是利用Handler异步消息处理机制,操作结果.使用Message回到主线程,从而执行UI更新线程. 而在我们的日常开发工作,Han ...

  7. [学习总结]5、Android的ViewGroup中事件的传递机制(二)

    下面是第一篇的连接 Android的ViewGroup中事件的传递机制(一) 关于onInterceptTouchEvent和onTouchEvent的详细解释. 1 public class Mai ...

  8. [学习总结]4、Android的ViewGroup中事件的传递机制(一)

    本文主要针对dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent三个方法,通过简单的例子来简单的介绍下. 根据字面意思的理解,dispatchTo ...

  9. C语言栈调用机制初探

    学习linux离不开c语言,也离不开汇编,二者之间的相互调用在源代码中几乎随处可见.所以必须清楚地理解c语言背后的汇编结果才能更好地读懂linux中相关的代码.否则会有很多疑惑,比如在head.s中会 ...

随机推荐

  1. java.util.concurrent包

    在JavaSE5中,JUC(java.util.concurrent)包出现了 在java.util.concurrent包及其子包中,有了很多好玩的新东西: 1.执行器的概念和线程池的实现.Exec ...

  2. JAVA-面向对象-特性

    1.封装 1.定义方式 1修饰符class类名 2类名首字母大写 2.类的成员 1属性 成员变量 可以设置默认值 第一个单词首字母小写,后面首字母大写 一般把属性设置成private 提供属性对应的g ...

  3. SQL 数据库 存储过程 视图

    一.存储过程 1.概述 存储过程是一组编译在单个执行计划中的T-SQL语句 存储过程:就像函数一样的会保存在数据库中(可编程性) 存储过程的优点: 1.允许模块化程序设计 2.允许更快执行如果某操作需 ...

  4. 一个新人对于JavaScript简单应用的理解

    JavaScript 1.输出:document.write("hello,world");    document.write的意思就是给我再此页面中显示出什么什么小括号里面的内 ...

  5. PHP判断手机号码是否合法

    <html> <head> <script language="JavaScript"> function checkMobile(input) ...

  6. override与final

    override 强调该函数是重写的父类的函数 final 指定该函数不能被重写 两者都是针对virtual 函数

  7. $.getJSON异步请求和同步请求

    先说一下我遇到的问题吧,我之前的一个函数想调用上一个函数的返回值,但是它的返回值一直为空,后来翻了一些资料才明白是异步请求在作怪,不多说,看例子,这是我之前有返回值函数的代码: function ge ...

  8. 03---Net基础加强

    多态---虚方法    (子类可以选择重写或者不重写) class Program { static void Main(string[] args) { Chinese cn1 = new Chin ...

  9. 夺命雷公狗---微信开发52----网页授权(oauth2.0)获取用户基本信息接口(2)

    我们在上一节课已经发送code给第三方了,那么这就要获取code去换取到用户的openid. 第一步:编写create_baseurl.php(上一节课程已经写完了) 第二步:编写vote1.php( ...

  10. 初级node+express建站

    我的建站经历. 我建站的原因也很简单,就仅仅想有一个linux服务器玩一玩,但是还没有想到要怎么玩,就先搭建一个web服务吧.因为我工作的时候可能会用到. 我就从头开始讲起走. 先买了个云服务器,选择 ...