容易忽略的一个view事件分发中的问题

我5月份找实习的时候被百度面试的一个问题,当时确实答了一半,还有一半没答上来,总结在了笔记本上了,现在拿出来整理了一下,详细拿出来说一下。

问题一:

  在button设置一个onTouchListener,设置一个onClickListener,在onTouch方法里根据动作输出对应的up,down动作,在onClick里边输出click。点击按钮,问输出的顺序。

问题二:

   如果把OnClickListener换成OnLongClickListener,长按按钮再松开,打印什么结果顺序?
   看到上面的问题,第一个问题相信很多人都能一次性答对,但是第二个问题就不一定了是吧,如果不是面试被问到,估计很难意识到这个问题,这种需要对比记忆理解的我觉得还是很有必要拿出来和大家分享一下的。
   好了,回到正题,回答问题

问题一:来,直接上个例子吧,先看结果

容易忽略的一个view事件分发中的问题_第1张图片
例子图

如上代码所示,定义了一个button,设置了touch和click两个点击事件,onTouch返回的是默认的false。在点击事件里打了Log方便看结果

容易忽略的一个view事件分发中的问题_第2张图片
手机界面图

很简单的布局,就不说明什么了哈。
点击一下按钮:结果如下

结果图

说明是先打印down,再打印UP,UP以后才发生click,这个从View事件分发的dispatchTouchEvent源码可以分析出来,相信再简单不过了。
延伸1:如果把onTouch返回true,结果会是怎样?

容易忽略的一个view事件分发中的问题_第3张图片
代码图

再点击一下按钮,结果图如下:
结果图

   相信不用解释了吧,dispatchTouchEvent里边的if语句直接满足三个条件,所以函数直接返回了true,消费了事件,所以不会走view的onTouchEvent吗,自然不会触发click事件。
问题二:
把OnClickListener换成OnLongClickListener
先解释一下这个函数吧,
public boolean onLongClick(View v)
   参数v:参数v为事件源控件,当长时间按下此控件时才会触发该方法。
   返回值:该方法的返回值为一个boolean类型的变量,当返回true时,表示已经完整地处理了这个事件,并不希望其他的回调方法再次进行处理;当返回false时,表示并没有完全处理完该事件,更希望其他方法继续对其进行处理。
   直接上代码图
容易忽略的一个view事件分发中的问题_第4张图片
代码图

容易忽略的一个view事件分发中的问题_第5张图片
界面图

点击一下按钮,看结果


点击一下--结果图

长按按钮别松开,看结果:


长按--结果图

这时候松开看结果:
松开--结果图

把onLongClick中的方法改成返回true;长按一会再松开。

容易忽略的一个view事件分发中的问题_第6张图片
结果图

依旧打印一样的结果。
OnLongClickListener的事件流程:
结论:
长按的调用栈:
onTouchEvent –case:ACTION_DOWN-checkForLongClick –post-
CheckForLongPress--run—performLongClick-.mOnLongClickListener.onLongClick
走一遍函数吧:

容易忽略的一个view事件分发中的问题_第7张图片
image.png

在onTouchEvent的MotionEvent.ACTION_DOWN,执行checkForLongClick

往下走
容易忽略的一个view事件分发中的问题_第8张图片
image.png
往下走
容易忽略的一个view事件分发中的问题_第9张图片
image.png
往下走
容易忽略的一个view事件分发中的问题_第10张图片
image.png
往下走
image.png
往下走

容易忽略的一个view事件分发中的问题_第11张图片
image.png

看到了吧,最后还是调用了我们设置的longclick点击事件。回顾一下,是在case的ACTION_DOWN分支中运行的这个点击。
所以最后点击了mOnLongClickListener的onLongClick
   延伸2:onClick和onLongClick能同时发生吗?
  要理解Android对事件处理的所谓消费(consume)概念即可,一个用户的操作会被传递到不同的View控件和同一个控件的不同监听方法处理,任何一个接收并处理了该次事件的方法如果在处理完后返回了true,那么该次event就算被完全处理了,其他的View或者监听方法就不会再有机会处理该event了。
   onLongClick的发生是由单独的线程完成的,一般发生在ACTION_UP之前,而onClick的发生是在ACTION_UP后。临界条件是同时发生,这里会有flag来进行区分。
   因此同一次用户touch操作就有可能既发生onLongClick又发生onClick。
及时向系统表示“我已经完全处理(消费)了用户的此次操作”,是很重要的事情。
另外一个同时执行的概念(都执行的意思),先后执行,例如,我们如果在onLongClick()方法的最后return true,那么onClick事件就没有机会被触发了
在onLongClick()方法return false的情况下,会有一次触碰操作的基本时序。
   如下问题3:
把onClick和onLongClick同时写入代码。

容易忽略的一个view事件分发中的问题_第12张图片
image.png

  可以看到我们写了ontouch,onclick,onlongclick三个点击。并且两个有个返回值的返回都是false。
  还是这个界面


容易忽略的一个view事件分发中的问题_第13张图片
界面图
  • 点击一下按钮,结果:


    image.png

    这个结果没啥解释的吧,就是view基本的事件传递模型。在UP分支中执行了


    image.png

    在它里边执行了li.mOnClickListener.onClick(this);
  • 长按一会再松开,结果:
容易忽略的一个view事件分发中的问题_第14张图片
image.png

从DOWN到UP,可以看出来前后发生了什么
  可以看到,在ACTION_UP后仍然触发了onClick()方法。onLongClick一般发生在ACTION_UP之前,而onClick的发生是在ACTION_UP后。
  如果此时把longClick返回true,其他代码不变。

容易忽略的一个view事件分发中的问题_第15张图片
image.png

再长按一下按钮松开,则打印:


image.png

发现不触发onClick了。当longclick把事件消费了以后,那么onClick事件就没有机会被触发了。
好了,写了这么多其实是个特别简单的问题,但是觉得还是应该分享一下,毕竟积少成多嘛,加油吧!骚年

你可能感兴趣的:(容易忽略的一个view事件分发中的问题)