有时会遇见这个问题:假设一个textview文本显示一个网址,程序中既给它注册长按事件操作,然后又会单击打开网页,也就是说既有onLongClick事件又有onClick事件。如果你只是点击一下,不会出问题,但如果你长按会发现在执行长按事件后也会执行单击事件,这是什么原因呢?接下来就进行分析对于view的触摸事件的执行,分析几两个问题,
为什么onClick时不会产生点击和长按的冲突?
为什么onLongClick时会执行完长按操作,再紧接着直接点击操作?
屏幕的滑动事件?
对于view的触摸事件有三个动作:
ACTION_DOWN:按下
ACTION_MOVE:移动
ACTION_UP:弹起
对于一个view,有touch事件,drag事件,click事件,所涉及到的listener方法如下
以TextView为例,给textview添加listener:
setOnTouchListener:覆写父接口OnTouchListener的onTouch方法,当触摸view时会触发该listener
setOnClickListener:覆写父接口OnClickListener的onClick方法,当点击view时会触发该listener
setOnLongClickListener:覆写父接口OnLongClickListener的onLongClick方法,当长按view时会触发该listener
public class MainActivity extends Activity implements View.OnClickListener,View.OnTouchListener, View.OnDragListener,View.OnLongClickListener{ private String TAG = "MainActivity"; private TextView mTestView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTestView = (TextView) findViewById(R.id.test_tv); mTestView.setOnTouchListener(this); mTestView.setOnClickListener(this); mTestView.setOnDragListener(this); mTestView.setOnLongClickListener(this); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG,"---onTouchEvent---"); return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(TAG,"---dispatchTouch--"); return super.dispatchTouchEvent(ev); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.test_tv: Log.i(TAG,"----onClick----"); break; default: break; } } @Override public boolean onTouch(View v, MotionEvent event) { Log.i(TAG,"----onTouch---"); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(TAG,"---onTouch--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"---onTouch--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"---onTouch--ACTION_UP"); break; } return false; } @Override public boolean onDrag(View v, DragEvent event) { Log.i(TAG,"---onDrag---"); return false; } @Override public boolean onLongClick(View v) { Log.i(TAG,"---onLongClick---"); return false; } }
还有dispatchTouchEvent:注意,这是覆写父类Activity的方法,为该activity中的控件的触摸事件进行分发,分发的意思也就是说,如果该方法返回true,当你对activity中的view进行点击,长按,滑动等操作时Log信息如下:
<span style="font-size:14px;">05-18 00:57:45.450 6229-6229/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:57:45.470 6229-6229/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:57:45.500 6229-6229/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:57:47.340 6229-6229/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:57:48.120 6229-6229/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch--</span>
如果返回false,则事件就会被分发到view。默认的是返回的false
在此声明:当屏幕进行触摸时首先是activity感受到该触摸事件,然后对事件进行分发处理,也就是说要不要传给activity中的view进行处理。在事件进行分发时,首先判断点击的位置是否处于view的范围,如果不属于会执行onTouchEvent方法,如果属于然后再分发到view。activity首先将事件分发到你所定义的最外层的view,在本程序中我只定义了一个view,所以当dispatchTouchEvent返回false进行事件分发时就理所当然的分发给了我所定义的view
当对view进行点击时Log如下:(此时dispatchTouch定义其返回false)
05-18 21:01:41.290 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 21:01:41.290 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 21:01:41.290 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_DOWN 05-18 21:01:41.320 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 21:01:41.320 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 21:01:41.330 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_MOVE 05-18 21:01:41.790 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---onLongClick--- 05-18 21:01:42.220 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 21:01:42.230 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 21:01:42.230 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_UP 05-18 21:01:42.230 24695-24695/com.fang.zrf.qrcodedemo I/MainActivity: ----onClick----
长按View事件
05-18 00:52:38.930 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:52:38.940 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 00:52:38.940 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_DOWN 05-18 00:52:39.440 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ---onLongClick--- 05-18 00:52:39.520 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:52:39.520 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 00:52:39.520 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_UP 05-18 00:52:39.520 28522-28522/com.fang.zrf.qrcodedemo I/MainActivity: ----onClick----
05-18 00:55:40.870 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:55:40.870 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 00:55:40.870 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_DOWN 05-18 00:55:41.380 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ---onLongClick--- 05-18 00:55:41.760 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 00:55:41.770 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 00:55:41.770 3373-3373/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_UP
通过这些log可以看出,当activity触摸时
首先进行dispatchTounchEvent进行事件的分发,分发到view后出发onTouchListener的onTouch方法,会有三个动作,如果是长按,则在ACTION_MOVE时(如果有move)离开控件之后,在ACTION_UP之前会触发onLongClick的listener,在ACTION_UP结束后会触发onClick方法。
了解到触摸事件处理逻辑后博文刚开始的问题就好解决多了
当点击view时,只是执行onClick,而不执行onLongClick
当长按view时 ,在手抬起之前执行onLongClick,在抬起之后会执行onClick,如果想要避免onClick的执行,只需要在onLongClick方法返回true,则onClick方法不会再执行,抬起之后onTouch还是会继续执行出现ACTION_UP
05-18 23:09:29.790 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 23:09:29.790 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 23:09:29.790 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_DOWN 05-18 23:09:30.300 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ---onLongClick--- 05-18 23:09:31.840 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 23:09:31.840 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 23:09:31.840 1008-1008/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_UP
05-18 23:11:26.150 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 23:11:26.160 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 23:11:26.160 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_DOWN 05-18 23:11:27.240 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ---dispatchTouch-- 05-18 23:11:27.240 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ----onTouch--- 05-18 23:11:27.240 3810-3810/com.fang.zrf.qrcodedemo I/MainActivity: ---onTouch--ACTION_UP