Android事件拦截机制

来源:互联网 发布:不用网络的搜题软件 编辑:程序博客网 时间:2024/06/05 03:12
  1. 触摸事件的类型
    触摸事件就是捕获触摸屏幕后产生的事件,比如当点击一个按钮会产生—-按钮按下,这是事件一,如果不小心滑动了,这是事件二,当手指抬起的时候,这是事件三。安卓提供了MotionEvent类,封装了触摸事件的类型。

    • ACTION_DOWN:用户手指的按下操作,一个按下操作代表着一次触摸事件的开始。
    • ACTION_MOVE:用户手指移动的操作,一般情况下,手指的轻微移动都会触发该方法。
    • ACTION_UP:手指抬起操作,一次抬起代表着一次触摸事件和结束。
  2. 事件传递的三个方法
    在事件传递具体实现之前,我们说下事件传递的三个方法

    • 事件分发方法

      public boolean dispatchTouchEvent(MotionEvent event)

      在安卓系统中,所有的触摸事件都是通过这个方法来分发的,在这个方法中,根据当前业务的实现逻辑,来决定是否消费这个事件还是将事件继续分发给子视图处理,方法返回值是true表示将当前事件消费掉,不再继续分发,当方法返回值是super.dispatchTouchEvent表示继续分发事件,当方法返回值是false,由父View消费事件。

    • 事件拦截

      public boolean onInterceptTouchEvent(MotionEvent event);

      这个方法只有在ViewGroup及其子类中才存在,在View和Activity中是不存在的。返回true表示拦截这个事件,不继续分发给子视图,同时由自身的onTouchEvent方法进行消费,返回false或者super.onInterceptTouchEvent表示不对事件进行拦截,会继续将事件传递给子视图。

    • 事件处理

      public boolean onTouchEvent(MotionEvent event)

      该方法返回true表示当前视图可以处理对应的事件,事件将不会向上传递给父视图中的onTouchEvent方法,返回值为false或者super.onTouchEvent(MotionEvent event)表示当前视图不处理这个事件,事件将会传递给父视图中的onTouchEvent方法进行处理。

    • 在Android系统中

      Activity:可以重写dispatchTouchEvent和onTouchEvent方法。ViewGroup:可以重写dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个方法。View:可以重写dispatchTouchEvent和onTouchEvent方法。
  3. View的事件传递机制
    上面已经说了View中可以重写dispatchTouchEvent和onTouchEvent方法,虽然ViewGroup的父类是View,但View中是没有onInterceptTouchEvent方法的。
    我们将自定义一个MyView1,在事件处理相关的方法中打印Log,来观察事件处理的流程。

    public class MyView1 extends View{private static final String TAG = "eventdemo";public MyView1(Context context) {    super(context);}public MyView1(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);}public MyView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {//        Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent");    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_UP");            break;        default:            break;    }    return super.dispatchTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {//        Log.i(TAG, getClass().getSimpleName() + " onTouchEvent");    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");            break;        default:            break;    }    return super.onTouchEvent(event);}}

    同时在Activity中设置点击和触摸回调:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {private static final String TAG = "eventdemo";private View myView1;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    myView1 = findViewById(R.id.myview1);    myView1.setOnClickListener(this);    myView1.setOnTouchListener(this);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//        Log.i("eventdemo", getClass().getSimpleName() + " dispatchTouchEvent");    switch (ev.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_UP");            break;        default:            break;    }    return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {//        Log.i("eventdemo", getClass().getSimpleName() + " onTouchEvent");    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");            break;        default:            break;    }    return super.onTouchEvent(event);}@Overridepublic void onClick(View v) {    Log.i(TAG, "myView1 onClick");}@Overridepublic boolean onTouch(View v, MotionEvent event) {    Log.i(TAG,  "myView1 onTouch");    return false;}}

    run代码点击MyView1后打印:
    这里写图片描述

  4. ViewGroup的事件传递机制
    我们将自定义一个MyGroupView1里面包含MyView1,在事件处理方法中打印Log,观察ViewGroup中的事件处理流程

        public class MyGroupView1 extends FrameLayout {private static final String TAG = "eventdemo";public MyGroupView1(@NonNull Context context) {    super(context);}public MyGroupView1(@NonNull Context context, @Nullable AttributeSet attrs) {    super(context, attrs);}public MyGroupView1(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {    super(context, attrs, defStyleAttr);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//        Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent");    switch (ev.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_UP");            break;        default:            break;    }    return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {    switch (ev.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_UP");            break;        default:            break;    }    return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");            break;        default:            break;    }    return super.onTouchEvent(event);}}

    run代码点击MyView1后打印:
    这里写图片描述
    这里的onInterceptTouchEvent方法没有打印出来,是由于设置了onClickListener消费了事件导致的。

    从上面观察的Log看我们可以得出:

    • 触摸事件的传递顺序是从Activity到ViewGroup,再由ViewGroup分发给子View的。
    • ViewGroup会通过onInterceptTouchEvent方法对事件选择是否拦截,如果该方法返回true,则事件将会被拦截并由ViewGrup中的onTouchEvent处理。否则事件将会继续传递到子View中。
    • 在子View中对事件进行消费后,ViewGroup将接收不到任何事件。
原创粉丝点击