当前位置: 首页 > 编程笔记 >

浅谈onTouch先执行,还是onClick执行(详解)

公羊凌
2023-03-14
本文向大家介绍浅谈onTouch先执行,还是onClick执行(详解),包括了浅谈onTouch先执行,还是onClick执行(详解)的使用技巧和注意事项,需要的朋友参考一下

有一个Button 按钮,要想为该按钮设置onClick事件和OnTouch事件

mTestButton.setOnClickListener(new View.OnClickListener() {  
      @Override 
      public void onClick(View view) {  
        Log.d(TAG, "onClick execute");  
      }  
});  
mTestButton.setOnTouchListener(new View.OnTouchListener() {  
      @Override 
      public boolean onTouch(View view, MotionEvent motionEvent) {  
        Log.d(TAG, "onTouch execute, action event " + motionEvent.getAction());  
        return false;  
      }  
});  

此时,我们现在分析一下,是onTouch先执行,还是onClick执行,接下来我从FrameWork 源码去探寻一下整个事件的执行流程和原理:

我们知道 Button ,TextView 等基础控件的基类都是View,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类(实际上是基类View)里的dispatchTouchEvent方法,所以接下来看View源码中dispatchTouchEvent()方法的具体实现:

public boolean dispatchTouchEvent(MotionEvent event) {  
  if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
      mOnTouchListener.onTouch(this, event)) {  
    return true;  
  }  
  return onTouchEvent(event);  
}  

分析上述代码,第2行 如果三个条件都为真的话,就返回true,否则执行onTouchEvent,先看第一个条件mOnTouchListener!=null,这个条件就是如果设置了OnTouchListener就会为true,否则是false; 第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true;第三个条件就比较复杂了,mOnTouchListener.onTouch(this, event),这个其实就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。onTouchEvent(MotionEvent event)方法同样也是在view中定义的一个方法,主要是处理传递到view 的手势事件,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL四种事件。

接下来我们结合上面的具体例子,来分析一下这个过程,首先会执行dispatchTouchEvent(MotionEvent event) ,所以onTouch方法肯定是早于onClick方法的,如果在onTouch里返回false,就会出现下面的现象:

10-20 18:57:49.670: DEBUG/MainActivity(20153): onTouch execute, action event 0
10-20 18:57:49.715: DEBUG/MainActivity(20153): onTouch execute, action event 1
10-20 18:57:49.715: DEBUG/MainActivity(20153): onClick execute

即先执行了onTouch,再执行了onClick事件,而且onTouch执行了两次,一个是action_down,一个是action_up事件;

如果onTouch里返回true,则出现下面的现象:

10-20 19:01:59.795: DEBUG/MainActivity(21010): onTouch execute, action event 0
10-20 19:01:59.860: DEBUG/MainActivity(21010): onTouch execute, action event 1

结果是onClick事件没有执行了,原因是如果onTouch返回true的话,则dispatchEvent(MotionEvent event)方法直接返回true了,相当于不往下传递事件了,所以onClick不会执行,相反如果onTouch返回false的话(此时会执行onClick方法),则会执行 onTouchEvent(MotionEvent event)方法,由此可以得出这样一个结论,onClick事件的具体调用执行肯定是在onTouchEvent(MotionEvent event)方法源码中,接下来分析一下该函数的源码:

public boolean onTouchEvent(MotionEvent event) { 
  final int viewFlags = mViewFlags; 
  if ((viewFlags & ENABLED_MASK) == DISABLED) { 
    // 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)); 
  } 
  if (mTouchDelegate != null) { 
    if (mTouchDelegate.onTouchEvent(event)) { 
      return true; 
    } 
  } 
  if (((viewFlags & CLICKABLE) == CLICKABLE || 
      (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 
    switch (event.getAction()) { 
      case MotionEvent.ACTION_UP: 
        boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; 
        if ((mPrivateFlags & 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 (!mHasPerformedLongPress) { 
            // 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) { 
            mPrivateFlags |= PRESSED; 
            refreshDrawableState(); 
            postDelayed(mUnsetPressedState, 
                ViewConfiguration.getPressedStateDuration()); 
          } else if (!post(mUnsetPressedState)) { 
            // If the post failed, unpress right now 
            mUnsetPressedState.run(); 
          } 
          removeTapCallback(); 
        } 
        break; 
      case MotionEvent.ACTION_DOWN: 
        if (mPendingCheckForTap == null) { 
          mPendingCheckForTap = new CheckForTap(); 
        } 
        mPrivateFlags |= PREPRESSED; 
        mHasPerformedLongPress = false; 
        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 
        break; 
      case MotionEvent.ACTION_CANCEL: 
        mPrivateFlags &= ~PRESSED; 
        refreshDrawableState(); 
        removeTapCallback(); 
        break; 
      case MotionEvent.ACTION_MOVE: 
        final int x = (int) event.getX(); 
        final int y = (int) event.getY(); 
        // Be lenient about moving outside of buttons 
        int slop = mTouchSlop; 
        if ((x < 0 - slop) || (x >= getWidth() + slop) || 
            (y < 0 - slop) || (y >= getHeight() + slop)) { 
          // Outside button 
          removeTapCallback(); 
          if ((mPrivateFlags & PRESSED) != 0) { 
            // Remove any future long press/tap checks 
            removeLongPressCallback(); 
            // Need to switch from pressed to not pressed 
            mPrivateFlags &= ~PRESSED; 
            refreshDrawableState(); 
          } 
        } 
        break; 
    } 
    return true; 
  } 
  return false; 
}

虽然源码有点多,但是我们只重点关注关键代码,在38行我们看到了代码:performClick();这个方法从名字表义来看就是OnClick方法的调用,我们进入到该方法中去看一探究竟,是否执行了OnClick方法呢?

public boolean performClick() {  
  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  if (mOnClickListener != null) {  
    playSoundEffect(SoundEffectConstants.CLICK);  
    mOnClickListener.onClick(this);  
    return true;  
  }  
  return false;  
} 

从上述代码可以看到,只要mOnClickListener不是null,就会去调用它的onClick方法,那mOnClickListener又是在哪里赋值的呢?经过分析后找到如下方法:

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

而上述这个方法就是我们在Application层经常使用的方法,即我们给button 设置点击事件的时候就会调用该方法了,分析到这了,我们知道了OnClick方法确实是在OnTouchEvent方法中,那么除了要设置 OnClickListener,调用onClick的条件又是什么呢?我们从38行代码往前推,从第14行可以分析出:

只要该控件是可点击的或者是长按类型的,则会进入到MotionEvent.ACTION_UP这个分支当中 ,然后经过各种条件判断,则会进入到38行的performClick()方法中。

至此,一切都清晰明白了!当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时或者长按时,都会在performClick()方法里回调被点击控件的onClick方法。

经验之谈:

关于OnTouchEvent(MotionEvent事件)事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。

以上这篇浅谈onTouch先执行,还是onClick执行(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍浅谈js的异步执行,包括了浅谈js的异步执行的使用技巧和注意事项,需要的朋友参考一下 1.Javascript语言的执行环境是”单线程”(single thread): 优点:实现起来比较简单,执行环境相对单纯; 缺点:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环)

  • 问题内容: 我们有一个基于石英的调度程序应用程序,该应用程序每分钟运行约1000个作业,每分钟的秒数均匀分布,即每秒约16-17个作业。理想情况下,这16-17个作业应同时触发,但是该作业的execute方法的第一个语句(仅记录执行时间)非常晚。例如,假设我们从05:00到05:04每分钟安排1000个作业。因此,理想情况下,计划在05:03:50进行的作业应该在05:03:50记录了execut

  • 问题内容: 我有一个包含单元测试的Go文件,其中一些使用了公共变量。我正在测试的代码中使用了另一个全局变量。所有这些都可能导致问题。 在Go中,当我们执行位于同一文件中的测试时,它们如何运行?并行还是下一个不会在前一个完成之前开始? 问题答案: 测试起来真的很容易: 使用运行它,输出显示它是顺序的: 因此,正常的测试是依次执行的,但是请不要忘记未定义顺序:如何依次运行golang测试? 还要注意,

  • 问题内容: 在Oracle SQL Developer中输入SQL语句时,我注意到我有两个选择。我可以选择“运行语句”或“运行脚本”。尽管命名为“ Execute query”和“ Execute as script”,但在SQL Maestro中似乎也可以使用类似的选择。 两者之间到底有什么区别? 问题答案: 将在可排序的表格中为您提供所有结果的列表。它还将仅在光标(或突出显示)下运行该语句。运

  • 问题内容: 我正在寻找一种仅单击 a-tag* 即可调用 PHP函数 的简单解决方案。 * PHP: HTML: 更新: html和PHP代码在同一个PHP文件中 问题答案: 首先,请了解您有三种语言一起使用: PHP:它仅由服务器运行,并响应诸如单击链接(GET)或提交表单(POST)之类的请求。 HTML和JavaScript:它只能在某人的浏览器中运行(NodeJS除外)。 我假设您的文件如

  • 问题内容: 我以为使用绑定绑定javascript中的click事件,Chrome/Firefox似乎同意我的看法,但是我看到它是3个人在这里写的4次,所以它不可能是错字,而且我怀疑这是错别字巧合。 那么,为什么人们在写作不起作用时又写作呢? 问题答案: 因为某些浏览器(取决于DOCTYPE)对inline属性是可以容忍的,所以它似乎已经散布开了,甚至出现在JavaScript问题中,因为大小写很