本文来自网易云社区

作者:孙有军

事件机制是Android中一个比较复杂且重要的知识点,比如你想自定义拦截事件,或者某系组件中嵌套了其他布局,往往会出现这样那样的事件冲突,坑爹啊!!事件主要涵盖onTouch,onClick,onTouchEvent,dispatchTouchEvent,onInterceptTouchEvent等等一系列事件,并且事件间还相互交互耦合,甚至有的事件还有返回值,一会true,一会false,什么情况下返回true,什么情况下返回false,为什么要有返回值,想想这些就感觉整个人都不好了。

但是(万恶的但是),该知识点还是必须要掌握的,知识的深度与广度决定了你走的远度,鉴于此我们就来捅一捅该知识点。

准备工作

俗话说工欲善其事必先利其器,为了看他的执行流程,我们还是先写个样例,打几个日志看看执行流程吧!

首先自定义一个外层布局的Layout,自定义Layout继承了LinearLayout,复写了相应的函数,在调用之前输入日志。如下:

public class Layout extends LinearLayout {
    public Layout(Context context) {
        super(context);
        init();
    }     public Layout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }     public Layout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }     private void init() {
        //requestDisallowInterceptTouchEvent(false);
    }     @TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("Event", "Layout onInterceptTouchEvent " + MotionEvent.actionToString(ev.getAction()));
        return super.onInterceptTouchEvent(ev);
    }     @TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("Event", "Layout onTouchEvent " + MotionEvent.actionToString(event.getAction()));
        return super.onTouchEvent(event);
    }     @TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("Event", "Layout dispatchTouchEvent " + MotionEvent.actionToString(event.getAction()));
        return super.dispatchTouchEvent(event);
    } }

我们还自定义了一个LogTextView,继承自TextView,也是为了输出日志,代码如下:

public class LogTextView extends TextView {

    public LogTextView(Context context) {
        super(context);
    }     public LogTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }     public LogTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }     @TargetApi(Build.VERSION_CODES.KITKAT)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("Event", "TextView  onTouchEvent " + MotionEvent.actionToString(event.getAction()));
        return super.onTouchEvent(event);
    }     @Override
    public void setOnTouchListener(OnTouchListener l) {
        super.setOnTouchListener(l);
    }     @Override
    public void setOnClickListener(OnClickListener l) {
        super.setOnClickListener(l);
    }
}

接下来是布局文件了:

<?xml version="1.0" encoding="utf-8"?>
<com.sunny.event.wigdet.Layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">     <com.sunny.event.wigdet.LogTextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="40dp"
        android:background="#999999"
        android:padding="20dp"
        android:text="Hello World!"/>     <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:src="@mipmap/ic_launcher"/>
</com.sunny.event.wigdet.Layout>

布局中嵌套了两个view,一个TextView,一个ImageView。最后就是主界面了。

public class MainActivity extends AppCompatActivity {

    private LogTextView tv;
    private ImageView imageView;     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        setViewListener();
    }     private void findViews() {
        tv = (LogTextView) findViewById(R.id.textView);
        imageView = (ImageView) findViewById(R.id.image);
    }     private void setViewListener() {
        tv.setOnTouchListener(new View.OnTouchListener() {
            @TargetApi(Build.VERSION_CODES.KITKAT)
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("Event", "TextView  onTouch " + MotionEvent.actionToString(event.getAction()));
                return true;
            }
        });
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("Event", "TextView  onClick ");
            }
        });
        imageView.setOnTouchListener(new View.OnTouchListener() {
            @TargetApi(Build.VERSION_CODES.KITKAT)
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("Event", "ImageView onTouch " + MotionEvent.actionToString(event.getAction()));
                return false;
            }
        });
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("Event", "ImageView onClick ");
            }
        });
    }
}

执行结果

round 1

TextView的onTouch返回为false,点击TextView,日志如下:

05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_DOWN
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onClick

根据日志我们可以看到首先有一个ACTION_DOWN事件,执行的顺序是Layout的dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch要→onTouchEvent,之后的我帕金森发生了,产生了ACTION_MOVE事件,传递的顺序与Down是一致的,最后一个事件是UP事件,正常点击不滑动是不会产生MOVE事件的,在这个这个三个事件最后调用了TextView的onClick事件。

小结:

  1. 事件的传递顺序是先外层容器,之后再是具体的View。

  2. onTouch事件先于onTouchEvent事件,onTouchEvent先于onClick事件

round 2

我们将TextView的onTouch事件返回true。重新执行。执行顺序如下:

05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_DOWN
05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_DOWN
05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_DOWN
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_MOVE
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_MOVE
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_MOVE
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_UP
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_UP
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_UP

从日志可以看出如果onTouch返回为true,执行顺序变成了如下:

首先还是ACTION_DOWN事件(Layout)dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch,ACTION_MOVE与ACTION_UP执行顺序同ACTION_DOWN,可以发现的是TextView的onTouchEvent事件没有了,并且onClick事件也没有了。

小结

1、onTouch事件的返回值为true会拦截onTouchEvent事件

2、onTouchEvent与onClick有关联

上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么?

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 HTTP/2部署使用

Android事件分发机制浅析(1)的更多相关文章

  1. Android事件分发机制浅析(3)

    本文来自网易云社区 作者:孙有军 我们只看最重要的部分 1: 事件为ACTION_DOWN时,执行了cancelAndClearTouchTargets函数,该函数主要清除上一次点击传递的路径,之后执 ...

  2. Android事件分发机制浅析(2)

    本文来自网易云社区 作者:孙有军 上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么? public boolean onInter ...

  3. Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

    Android事件分发机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 Android事件分发机制的发生在Vi ...

  4. Android事件分发机制(下)

    这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...

  5. Android事件分发机制(上)

    Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...

  6. android事件分发机制

    android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...

  7. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  8. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  9. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

随机推荐

  1. linux一切皆文件之文件描述符

    一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件.如:普通文件.目录.字符设备.块设备.套接字等2.当一个文件被进程打开,就会创建一个文件描述符.这时候,文件的路径就成 ...

  2. docker-2-安装

    安装之前确定Centos的相关问题: CentOS Docker 安装 Docker支持以下的CentOS版本: CentOS 7 (64-bit) CentOS 6.5 (64-bit) 或更高的版 ...

  3. PAT——1012. 数字分类

    给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字: A1 = 能被5整除的数字中所有偶数的和: A2 = 将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2+n3-n4...: ...

  4. String.prototype是什么?

    String.prototype用于为某字符串对象新增方法,比如: 在javascript中有一方法replace,它是用于替换某字符串中第一个匹配的字符,如果我们想为它追加一个循环匹配所有字符的方法 ...

  5. ViewController里的loadView和viewDidLoad什么区别

    当你访问一个ViewController的view属性时,如果此时view的值是nil,那么,ViewController就会自动调用loadView这个方法.这个方法就会加载或者创建一个view对象 ...

  6. STM32之定时器输入捕获

    1.输入捕获模式可以用来测量脉冲宽度或者测量频率.STM32的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能.STM32的输入捕获,简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿 ...

  7. Oracle死锁一例(ORA-00060),锁表导致的业务死锁问题

    1.问题发现 检查客户数据库的时候发现存在大量死锁的情况 Thread advanced to log sequence (LGWR switch) Current log# mem# : /orad ...

  8. Swift_枚举

    Swift_枚举 点击查看源码 空枚举 //空枚举 enum SomeEnumeration { // enumeration definition goes here } 枚举基本类型 //枚举基本 ...

  9. iOS安装CocoaPods详细过程

    iOS安装CocoaPods详细过程 一.简介 什么是CocoaPods CocoaPods是OS X和iOS下的一个第三类库管理工具,通过CocoaPods工具我们可以为项目添加被称为“Pods”的 ...

  10. 【2018 ICPC亚洲区域赛徐州站 A】Rikka with Minimum Spanning Trees(求最小生成树个数与总权值的乘积)

    Hello everyone! I am your old friend Rikka. Welcome to Xuzhou. This is the first problem, which is a ...