Android事件拦截机制

本文详细介绍了Android中的触摸事件,包括ACTION_DOWN、ACTION_MOVE和ACTION_UP等类型,以及事件传递过程中的dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个关键方法的作用与实现方式。
摘要由CSDN通过智能技术生成
  1. 触摸事件的类型
    触摸事件就是捕获触摸屏幕后产生的事件,比如当点击一个按钮会产生----按钮按下,这是事件一,如果不小心滑动了,这是事件二,当手指抬起的时候,这是事件三。安卓提供了MotionEvent类,封装了触摸事件的类型。

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

    1. 事件分发方法
    public boolean dispatchTouchEvent(MotionEvent event)
    
    在安卓系统中,所有的触摸事件都是通过这个方法来分发的,在这个方法中,根据当前业务的实现逻辑,来决定是否消费这个事件还是将事件继续分发给子视图处理,方法返回值是true表示将当前事件消费掉,不再继续分发,当方法返回值是super.dispatchTouchEvent表示继续分发事件,当方法返回值是false,由父View消费事件。
    
    1. 事件拦截
    public boolean onInterceptTouchEvent(MotionEvent event);
    
    这个方法只有在ViewGroup及其子类中才存在,在View和Activity中是不存在的。返回true表示拦截这个事件,不继续分发给子视图,同时由自身的onTouchEvent方法进行消费,返回false或者super.onInterceptTouchEvent表示不对事件进行拦截,会继续将事件传递给子视图。
    
    1. 事件处理
    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);
    }


    @Override
    public 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);
    }

    @Override
    public 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;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView1 = findViewById(R.id.myview1);
        myView1.setOnClickListener(this);
        myView1.setOnTouchListener(this);
    }

    @Override
    public 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);
    }


    @Override
    public 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);
    }

    @Override
    public void onClick(View v) {
        Log.i(TAG, "myView1 onClick");
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.i(TAG,  "myView1 onTouch");
        return false;
    }
}

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

  1. 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);
    }

    @Override
    public 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);
    }


    @Override
    public 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);
    }

    @Override
    public 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将接收不到任何事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值