Android中单个View的触摸事件分发机制

2022-05-07 14:58:47 浏览数 (1)

有时会遇见这个问题:假设一个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

代码语言:javascript复制
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信息如下:

代码语言:javascript复制
<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>

也就是说返回true的话,点击,长按,滑动事件不会被分发到view的listener中,不会去执行任何操作,也就是触摸事件到这里就截止了,不会再往下传。

如果返回false,则事件就会被分发到view。默认的是返回的false

在此声明:当屏幕进行触摸时首先是activity感受到该触摸事件,然后对事件进行分发处理,也就是说要不要传给activity中的view进行处理。在事件进行分发时,首先判断点击的位置是否处于view的范围,如果不属于会执行onTouchEvent方法,如果属于然后再分发到view。activity首先将事件分发到你所定义的最外层的view,在本程序中我只定义了一个view,所以当dispatchTouchEvent返回false进行事件分发时就理所当然的分发给了我所定义的view

当对view进行点击时Log如下:(此时dispatchTouch定义其返回false)

代码语言:javascript复制
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事件

代码语言:javascript复制
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----

如果onLongClickListener返回为false,则log如下

代码语言:javascript复制
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

代码语言:javascript复制
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

但是如果onTouch方法返回true的话,那么触摸事件就不会传递给onClick或者onLongClick方法,此时就相当于只有滑动事件没有点击事件,log如下

代码语言:javascript复制
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

0 人点赞