博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android触摸事件的酸甜苦辣以及详细介绍
阅读量:5923 次
发布时间:2019-06-19

本文共 10746 字,大约阅读时间需要 35 分钟。

一、前言

一次完整的事件传递主要包括三个阶段,分别是事件的分发、拦截和消费。

二、事件传递的三个阶段

  • 分发(Dispatch):事件的分发对应着dispatchTouchEvent方法,在Android系统中,所有触摸事件都是通过这个方法来分发的,代码:
    public boolean dispatchTouchEvent(MotionEvent ev)复制代码

在这个方法中,根据当前视图的具体实现逻辑,来决定是直接消费这个事件还是将事件继续分发给子视图处理,方法返回值为true表示事件被当前视图消费掉,不再继续分发事件;方法返回值为super.dispatchTouchEvent表示继续分发该事件。如果当前视图是ViewGroup及其子类,则会调用onInterceptTouchEvent方法判定是否拦截该事件。

  • 拦截(Intercept):事件的拦截对应着onInterceptTouchEvent方法,这个方法只在ViewGroup及其子类中才存在,在View和Activity中是不存在的,代码:
    public boolean onInterceptTouchEvent(MotionEvent ev)复制代码

这个方法也是通过返回的布尔值来决定是都拦截对应的事件,根据具体的实现逻辑,返回true表示拦截这个事件,不继续分发给子视图,同时交由自身的onTouchEvent方法进行消费;返回false或者super.onInterceptTouchEvent表示不对事件进行拦截,需要继续传递给子视图。

  • 消费(Consume):事件的消费对应着onTouchEvent方法,代码:
    public boolean onTouchEvent(MotionEvent event)复制代码

该方法返回值为true表示当前视图可以处理对应的事件,事件将不会向上传递给父视图;返回值为false表示当前视图不处理这个事件,事件会被传递给父视图的onTouchEvent方法进行处理。

在Android系统中,拥有事件传递处理能力的类有以下三种:

  • Activity:拥有dispatchTouchEvent和onTouchEvent两个方法。
  • ViewGroup:拥有dispatchTouchEvent、onIntercptTouchEvent和onTouchEvent三个方法。
  • View:拥有dispatchTouchEvent和onTouchEvent两个方法。

三、View的事件传递机制

虽然ViewGroup是View的子类,这里所说的View专指除ViewGroup外的View控件,例如TextView、Button、CheckBox等,View控件本身已经是最小的单位,不能再作为其他View的容器。View控件拥有dispatchTouchEvent和onTouchEvent两个方法。首先定义一个集成TextView的类MyTextView,如下:

public class MyTextView extends TextView {    private static final String TAG = "MyTextView";    public MyTextView(Context context) {        super(context);    }    public MyTextView(Context context, AttributeSet attrs) {        super(context, attrs);    }        @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "dispatchTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "onTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "onTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "onTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.onTouchEvent(event);    }}复制代码

然后在MainActivity展示MyTextView,为Activity中的MyTextView设置点击(onClick)和触摸(onTouch)监听,方便跟踪事件传递流程,如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {    private static final String TAG = "MainActivity";    private MyTextView mTextView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (MyTextView) findViewById(R.id.my_text_view);        mTextView.setOnClickListener(this); // 设置MyTextView的点击处理        mTextView.setOnTouchListener(this); // 设置MyTextView的触摸处理    }        @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "dispatchTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "onTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "onTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "onTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.onTouchEvent(event);    }    @Override    public void onClick(View view) {        switch (view.getId()) {            case R.id.my_text_view:                Log.e(TAG, "MyTextView onClick");                break;            default:                break;        }    }    @Override    public boolean onTouch(View view, MotionEvent motionEvent) {        switch(view.getId()) {            case R.id.my_text_view:                switch (motionEvent.getAction()) {                    case MotionEvent.ACTION_DOWN:                        Log.e(TAG, "MyTextView onTouch ACTION_DOWN");                        break;                    case MotionEvent.ACTION_MOVE:                        Log.e(TAG, "MyTextView onTouch ACTION_MOVE");                        break;                    case MotionEvent.ACTION_UP:                        Log.e(TAG, "MyTextView onTouch ACTION_UP");                        break;                    default:                        break;                }                break;            default:                break;        }        return false;    }}复制代码

运行代码后,点击MyTextView打印的日志:

com.mrxi.viewdemo E/MainActivity: dispatchTouchEvent ACTION_DOWN

com.mrxi.viewdemo E/MyTextView: dispatchTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MainActivity: MyTextView onTouch ACTION_DOWN
com.mrxi.viewdemo E/MyTextView: onTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MainActivity: dispatchTouchEvent ACTION_UP
com.mrxi.viewdemo E/MyTextView: dispatchTouchEvent ACTION_UP
com.mrxi.viewdemo E/MainActivity: MyTextView onTouch ACTION_UP
com.mrxi.viewdemo E/MyTextView: onTouchEvent ACTION_UP
com.mrxi.viewdemo E/MainActivity: MyTextView onClick
从上面的代码和运行的日志可以看出,dispatchTouchEvent、onTouchEvent这两个方法的返回值可能存在以下三种情况:

  • 直接返回false
  • 直接返回true
  • 返回父类的同名方法,例如:super.dispatchTouchEvent

除此之外还得出结论:

  • 触摸事件的传递流程是从dispatchTouchEvent开始的,如果不进行人为干预(默认返回父类同名函数),则事件将会依照嵌套层次从外层向最内层传递,到达最内层的View时,就由它的onTouchEvent方法处理,该方法如果能够消费掉该事件,则返回true,如果处理不了,则返回false,这时事件会重新向外层传递,并由外层View的onTouchEvent方法进行处理。
  • 如果事件在向内层传递过程中由于人为干预,事件处理函数返回true,则会导致事件提前被消费掉,内层View将不会收到这个事件。
  • View控件的事件触发顺序是先执行onTouch方法,在最后才执行onClick方法。如果onTouch返回true,则事件不会继续传递,最后也不会调用onClick方法,如果onTouch放回false,则事件继续传递。

四、ViewGroup的事件传递机制

ViewGroup的作为View控件的容器存在的,Android系统默认提供了一系列ViewGroup的子类,常见的有LinearLayout、RelativeLayout、FrameLayout、ListView、ScrollView等。ViewGroup拥有dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三个方法,可以看出和View的唯一区别是多了一个onInterceptTouchEvent方法。自定义一个ViewGroup,继续成RelativeLayout,实现一个MyRelativeLayout,如下:

public class MyRelativeLayout extends RelativeLayout {    private static final String TAG = "MyRelativeLayout";    public MyRelativeLayout(Context context) {        super(context);    }    public MyRelativeLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "dispatchTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "onInterceptTouchEvent ACTION_UP");                break;            default:                break;        }        return false;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                Log.e(TAG, "onTouchEvent ACTION_DOWN");                break;            case MotionEvent.ACTION_MOVE:                Log.e(TAG, "onTouchEvent ACTION_MOVE");                break;            case MotionEvent.ACTION_UP:                Log.e(TAG, "onTouchEvent ACTION_UP");                break;            case MotionEvent.ACTION_CANCEL:                Log.e(TAG, "onTouchEvent ACTION_CANCEL");                break;            default:                break;        }        return super.onTouchEvent(event);    }}复制代码

同样将这个Layout作为MyTextView的容器,修改的xml布局文件:

复制代码

运行MyTextView后的日志:

com.mrxi.viewdemo E/MainActivity: dispatchTouchEvent ACTION_DOWN

com.mrxi.viewdemo E/MyRelativeLayout: dispatchTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MyTextView: dispatchTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MainActivity: MyTextView onTouch ACTION_DOWN
com.mrxi.viewdemo E/MyTextView: onTouchEvent ACTION_DOWN
com.mrxi.viewdemo E/MainActivity: dispatchTouchEvent ACTION_UP
com.mrxi.viewdemo E/MyRelativeLayout: dispatchTouchEvent ACTION_UP
com.mrxi.viewdemo E/MyRelativeLayout: onInterceptTouchEvent ACTION_UP
com.mrxi.viewdemo E/MyTextView: dispatchTouchEvent ACTION_UP
com.mrxi.viewdemo E/MainActivity: MyTextView onTouch ACTION_UP
com.mrxi.viewdemo E/MyTextView: onTouchEvent ACTION_UP
com.mrxi.viewdemo E/MainActivity: MyTextView onClick
可以看到唯一不一样的,与View的事件流程不一样的额地方是MainActivity和MyTextView之间增加了一层MyRelativeLayout。根据日志可以分析如下:

  • 触摸事件的传递顺序是由Activity的ViewGroup,再由ViewGroup递归传递给它的子View。
  • ViewGroup通过onInterceptTouchEvent方法对事件进行拦截,如果该方法返回true,则事件不会继续传递给子View,如果返回false或者super.onInterceptTouchEvent,则事件会继续传递给子View。
  • 在子View中对事件进行消费后,ViewGroup将接受不到任何事件。

转载地址:http://nrivx.baihongyu.com/

你可能感兴趣的文章
全球物联网专利竞争态势分析
查看>>
《网络安全法》为大数据保驾护航
查看>>
微软Edge浏览器开始支持WebVR
查看>>
QDialog之屏蔽Esc键
查看>>
CSS3之创建透明边框三角
查看>>
Android 多媒体库新漏洞 2.75亿部设备受影响
查看>>
《I'm a Mac:雄狮训练手册》——0.2 OS X
查看>>
《Splunk智能运维实战》——1.8 定义字段提取内容
查看>>
《UNIX网络编程 卷1:套接字联网API(第3版)》——2.8 SCTP关联的建立和终止...
查看>>
前甲骨文 Linux 部门主管加入微软
查看>>
深度学习项目实战——“年龄预测”
查看>>
《为自己工作——世界顶级设计师成功法则》—第3章3.1节什么是利基
查看>>
Mybatis调用MySQL存储过程的简单实现
查看>>
《CUDA C编程权威指南》——3.8节习题
查看>>
《R的极客理想—工具篇》—— 1.3 fortunes 记录R语言的大智慧
查看>>
《识数寻踪:WinHex应用与数据恢复开发秘籍》——第1章 学海茫茫孤帆冷——数据恢复概述 1.1 给所有数据恢复工程师的话...
查看>>
《抓住听众心理——演讲者要知道的100件事》一14.遗忘是程序化的
查看>>
《R数据可视化手册》一3.9 添加数据标签
查看>>
淘富成真|这次带来了女性的护肤神器
查看>>
《Unity 5.x游戏开发实战》一2.1 创建一个金币的材质
查看>>