Android事件分发机制[-View-] 源码级

栏目: Android · 发布时间: 5年前

内容简介:布局树如下:现在自定义一个单体View,一个ViewGroup图例部分说过了,这里不废话了在Activity的
1.有三东西挺长的,又长得挺像,看着晃眼且心烦,文中以下简写为:
|-- 分发 dispatchTouchEvent = d16t       
|-- 截断 onInterceptTouchEvent = o19t    
|-- 消费 onTouchEvent = o10t             


2.事件分发机制的参与者与各自拥有的回调方法:
|-- 灰色 Activity:  o10t    d16t        
|-- 紫色 ViewGroup: o10t    d16t   o19t
|-- 橙色 单体View:  o10t    d16t

3.MotionEvent的几种常见时间
|-- MotionEvent.ACTION_DOWN = 0;    按下
|-- MotionEvent.ACTION_UP = 1;      抬起
|-- MotionEvent.ACTION_MOVE=2;      移动
|-- MotionEvent.ACTION_CANCEL=3;    取消       
复制代码

图例这里给出来,了解一下

下面表示:触点在Activity上,
按下事件(即0)触发了 d16t(即dispatchTouchEvent) 
然后按下事件(即0)触发了 o10t( 即onTouchEvent)
抬起事件(即1)触发了 d16t(即dispatchTouchEvent) 
然后抬起事件(即1)触发了 o10t( 即onTouchEvent)  ---我想这样应该表述的淋漓尽致了
复制代码
Android事件分发机制[-View-] 源码级

一、正常情况下的事件传递

0.自定义两个测试View

布局树如下:现在自定义一个单体View,一个ViewGroup

Android事件分发机制[-View-] 源码级
/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/2/21/021:9:11<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:事件测试Activity
 */
public class EventActivity extends AppCompatActivity {
    private static final String TAG = "EventTest";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_test);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent:--" + event.getAction() + " --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
        return super.onTouchEvent(event);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
        return super.dispatchTouchEvent(ev);
    }
}

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/2/21/021:9:06<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:事件测试View单体
 */
public class SonView extends View {
    private static final String TAG = "EventTest";
    public SonView(Context context) {
        super(context);
    }
    public SonView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setBackgroundColor(0xffE58F46);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
        return super.dispatchTouchEvent(event);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "--  ††††††††††††††††††††††††††††††††††††††††");
        return super.onTouchEvent(event);
    }
}

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/2/21/021:9:06<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:事件测试ViewGroup
 */
public class FatherViewGroup extends FrameLayout {
    private static final String TAG = "EventTest";
    public FatherViewGroup(Context context) {
        super(context);
    }
    public FatherViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        setBackgroundColor(0xff9869B7);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  ");
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + "  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
        return super.onTouchEvent(event);
    }
}
复制代码

1.在两View之外点击

图例部分说过了,这里不废话了

Android事件分发机制[-View-] 源码级
Android事件分发机制[-View-] 源码级
2019-02-21 10:13:25.773 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.775 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

2.点击紫色ViewGroup

在Activity的 d16t 时,之后,事件到了ViewGroup里

Android事件分发机制[-View-] 源码级
Android事件分发机制[-View-] 源码级
2019-02-21 10:36:34.040 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.040 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 10:36:34.040 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 10:36:34.041 : FatherViewGroup--onTouchEvent:--0  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 10:36:34.041 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

3.点击橙色View单体

在ViewGroup的 o18t 时,之后,事件到了View里,

这里感觉像是...一根链条。万一哪块掉链子了会怎么样?

Android事件分发机制[-View-] 源码级
Android事件分发机制[-View-] 源码级
2019-02-21 10:46:15.721 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.722 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 10:46:15.722 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 10:46:15.722 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.723 : SonView--onTouchEvent:--0--  ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.724 : FatherViewGroup--onTouchEvent:--0  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 10:46:15.726 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.772 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.773 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

二、掉链子测试

1. d16tdispatchTouchEvent :

口号: 宁为玉碎不为瓦全,我得不到的,你也别想得到!

1.1.Activity的 d16t 掉链子: 宁为玉碎不为瓦全

Activity说: 哥不爽了,事件不给你们玩!d16t 返回 false. 说完把event扔了

这链子一断...就算点击View单体,事件也传不下去了。

Android事件分发机制[-View-] 源码级
---->[EventActivity#dispatchTouchEvent]------------------------------
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
    return false;
}


2019-02-21 11:01:19.109 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:01:19.159 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

1.2.ViewGroup的 d16t 掉链子

现在回到 初始情况 ,Activity把事件给了ViewGroup玩,ViewGroup说: 爷也不爽了! 然后一丢

测试中看来:在ViewGroup的 d16t 返回false之后会回调Activity的 o10T

Android事件分发机制[-View-] 源码级
---->[FatherViewGroup#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
    return false;
}

2019-02-21 11:27:07.487: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.488: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 11:27:07.489: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

1.3.View单体的 d16t 掉链子

现在回到 初始情况

Android事件分发机制[-View-] 源码级
---->[SonView#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
    return false;
}

2019-02-21 11:28:32.080: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.081: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 11:28:32.081: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 11:28:32.081: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 11:28:32.082: FatherViewGroup--onTouchEvent:--0  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 11:28:32.083: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

1.4 小结一下 d16t

从测试上看到的结论:

dispatchTouchEvent的作用在于分发事件,源头出发不走,后面都白忙活 
只要发走了,后面哪里断掉,就会触发上一级的 o10t 
复制代码

2. o18t 对事件的影响

作为ViewGroup独有的方法,onInterceptTouchEvent可以决定事件是否打断

Android事件分发机制[-View-] 源码级
---->[FatherViewGroup#onInterceptTouchEvent]--------------------
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  ");
    return true;
}

2019-02-21 11:54:46.648 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.649 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 11:54:46.649 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 11:54:46.650 : FatherViewGroup--onTouchEvent:--0  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 11:54:46.652 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
复制代码

3. o10t 掉链子

3.1:当ViewGroup消费事件

默认情况下由最顶层消费事件,这里只有让当ViewGroup消费事件,事件就不会往下传递了

Android事件分发机制[-View-] 源码级
---->[FatherViewGroup#onTouchEvent]--------------------
 @Override
 public boolean onTouchEvent(MotionEvent event) {
     Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + "  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
     return true;
 }

2019-02-21 12:09:06.605 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: SonView--onTouchEvent:--0--  ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--0  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 12:09:06.622 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--1  --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
复制代码

3.2:当子View消费事件

Android事件分发机制[-View-] 源码级
---->[SonView#onTouchEvent]--------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "--  ††††††††††††††††††††††††††††††††††††††††");
    return true;
}

2019-02-21 12:10:05.682 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.683 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 12:10:05.683 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 12:10:05.683 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.683 : SonView--onTouchEvent:--0--  ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.724 : FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ 
2019-02-21 12:10:05.724 : FatherViewGroup--onInterceptTouchEvent:--1 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯  
2019-02-21 12:10:05.724 : SonView--dispatchTouchEvent:1-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : SonView--onTouchEvent:--1--  ††††††††††††††††††††††††††††††††††††††††
复制代码

3.3 小结一下 o10t

从测试上看到的结论:

onTouchEvent的作用在于消费事件,消费之后不会再往下传递
这里还是想强调一下[d16t]和[o10t]的区别,一者消费,一者分发
由于消费在分发之前,消费是不会阻碍分发的,但分发会影响消费

老婆(Activity):给你100块当做一月生活费,这叫分发dispatchTouchEvent,生活费相当MotionEvent
你(ViewGroup):拿到这100块,可以决定是否把这100块给儿子(View)当生活费(dispatchTouchEvent)
当决定给了,但是中途还可以通过onInterceptTouchEvent打断给的念头...

|--儿子拿到钱花了,钱就没了
|--儿子拿到钱不花,就被老爸没收了,老爸花了,钱就没了
|--儿子拿到钱不花,就被老爸没收了,老爸没花,就被老婆没收了,老婆花了,钱就没了  
差不多就是这个理,默认下你和儿子都是不敢花的,但都在手里过了一遍
后面人有没有得花,首先要看老婆给不给,不给,后面就没戏了...

复制代码

上面如果理清楚,使用方面应该就没问题了

二、源码查看

1.Activity和ViewGroup中的 dispatchTouchEvent

Android事件分发机制[-View-] 源码级
---->[Activity#dispatchTouchEvent]----------------------------
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
--->if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
--->return onTouchEvent(ev);
}

|---这里可以很清楚的看清Activity的onTouchEvent回调的时机即
    getWindow().superDispatchTouchEvent(ev)  返回false时触发
|---getWindow这里就不废话了,前面都说过,是PhoneWindow对象,直接进

---->[PhoneWindow#dispatchTouchEvent]----------------------------
|--- 调用的是mDecor的方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

---->[PhoneWindow$DecorView#superDispatchTouchEvent]----------------------------
|--- 这皮球踢得...mDecor是DecorView对象,继承自FrameLayout,在上一辈便是ViewGroup
|--- 所以这样看来,Activity的dispatchTouchEvent的本质也是ViewGroup触发的
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

---->[ViewGroup#superDispatchTouchEvent]----------------------------
|---这个方法大概100多行
/**
 * {@inheritDoc}
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
--->    final boolean intercepted;//是否打断
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
--->            intercepted = onInterceptTouchEvent(ev);//触发onInterceptTouchEvent,默认false
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            intercepted = true;
        }

        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }

        // Check for cancelation.
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
--->    if (!canceled && !intercepted) {//未被取消并且未被打断
            ...
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                ...
                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    ...
                    //这里接了一下mChildren,mChildren的初始化及添加操作稍后分析
                    final View[] children = mChildren;
        --->        for (int i = childrenCount - 1; i >= 0; i--) {//这里开始遍历所有的孩子
                        final int childIndex = customOrder
                                ? getChildDrawingOrder(childrenCount, i) : i;
                        final View child = (preorderedList == null)
                                ? children[childIndex] : preorderedList.get(childIndex);
                        ...
                        newTouchTarget = getTouchTarget(child);
                        ...
                        resetCancelNextUpFlag(child);
                        //dispatchTransformedTouchEvent会触发子View的d16t方法
                --->    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            // Child wants to receive touch within its bounds.---孩子想要在他的范围内接受触摸
                            ...
               --->        newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                        ...
        // Dispatch to touch targets. ---- 分发到多个触摸点?...
        if (mFirstTouchTarget == null) {//mFirstTouchTarget在addTouchTarget方法中被赋值
--->        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            //下面遍历target的链表,取出链表中的View执行dispatchTransformedTouchEvent
            while (target != null) {
                final TouchTarget next = target.next;
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
        --->        if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }
        ....
         return handled;
}

---->[ViewGroup#dispatchTransformedTouchEvent]----------------------------
|--总的来说就是触发super或是child的d16t方法,ViewGroup的super是谁?
|--答:View 。child 如果不为空走自己的d16t,如果child还是ViewGroup,就再走一圈上面的
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
...
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {//取消时
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
--->        handled = super.dispatchTouchEvent(event);//孩子为空触发super的d16t,
        } else {
            handled = child.dispatchTouchEvent(event);//孩子不为空,触发d16t
        }
        event.setAction(oldAction);
        return handled;
    }

    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            if (child == null) {
    --->        handled = super.dispatchTouchEvent(event);
            } else {
                ...
    --->        handled = child.dispatchTouchEvent(event);
            }
            return handled;
        }
        transformedEvent = MotionEvent.obtain(event);
    } else {
        transformedEvent = event.split(newPointerIdBits);
    }

    if (child == null) {
--->    handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        ...
--->    handled = child.dispatchTouchEvent(transformedEvent);
    }
    transformedEvent.recycle();
    return handled;
}


---->[ViewGroup#addTouchTarget]----------------------------
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
    TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    target.next = mFirstTouchTarget;
    mFirstTouchTarget = target;
    return target;
}

---->[TouchTarget]----------------------------
|---描述触摸的View以及手指的id们
|---TouchTarget是ViewGroup的一个内部类,看样子是一个单链表
|---有一个next的TouchTarget字段,链表承载数据类型是View
private static final class TouchTarget {
    ...
    public View child;
    public TouchTarget next;
    ...
}

---->[ViewGroup$TouchTarget#obtain]----------------------------
|-- 这里很明显是第一个元素出链表
public static TouchTarget obtain(View child, int pointerIdBits) {
    final TouchTarget target;
    synchronized (sRecycleLock) {
        if (sRecycleBin == null) {
            target = new TouchTarget();
        } else {
            target = sRecycleBin;
            sRecycleBin = target.next;
            sRecycledCount--;
            target.next = null;
        }
    }
    target.child = child;
    target.pointerIdBits = pointerIdBits;
    return target;
}

---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return false;
}
复制代码

2.View的 dispatchTouchEvent

看这个松了一口气...

public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.isTargetAccessibilityFocus()) {
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        event.setTargetAccessibilityFocus(false);
    }
    boolean result = false;
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }

    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        stopNestedScroll();
    }

    if (onFilterTouchEventForSecurity(event)) {
         //注意,这四个条件满足,直接返回true,就不会触发onTouchEvent了
         //并且会触发mOnTouchListener的onTouch回调
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
--->            && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
--->    if (!result && onTouchEvent(event)) {//这里触发了View的onTouchEvent!!!,感动...
            result = true;
        }
    }

    if (!result && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
    }

    if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        stopNestedScroll();
    }
    return result;
}
复制代码

4.关于 onInterceptTouchEvent

也许你没注意,刚才已经被消灭了...它只是用来控制的boolean而已

---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return false;
}
复制代码

5.关于: onTouchEvent

---->[Activity#onTouchEvent]-----------------------
|--少得可怜,基本上就是false了
public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }
    return false;
}

---->[ViewGroup#onTouchEvent]-----------------------
|--咦,我的onTouchEvent呢?竟然没有!!!!
|--这让我挺意外,也就是ViewGroup完全使用View的onTouchEvent

---->[View#onTouchEvent]-----------------------
|--就这个有点说头...这里追踪一下点击事件
public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();

    if ((viewFlags & ENABLED_MASK) == DISABLED) {//表示不可用
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        return (((viewFlags & CLICKABLE) == CLICKABLE //直接滚回
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
    }

    if (mTouchDelegate != null) {//有了触摸代理
        if (mTouchDelegate.onTouchEvent(event)) {//执行代理人的onTouchEvent
            return true;////直接滚回
        }
    }
    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {//满足上面一堆情况
        switch (action) {//我们最熟悉的 switch (action) 
            case MotionEvent.ACTION_UP://抬起时
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    boolean focusTaken = false;
                    ...
                    if (prepressed) {//被按下
                        setPressed(true, x, y);//标true
                   }
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // This is a tap, so remove the longpress check--这是一个短点击,所以去掉长按检查
                        removeLongPressCallback();
                        if (!focusTaken) {
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClick();//执行点击
                            }
                        }
                    }
                   ...
                break;
            case MotionEvent.ACTION_DOWN:
                ...
                break;
            case MotionEvent.ACTION_CANCEL:
                ...
                break;

            case MotionEvent.ACTION_MOVE:
                ...
                break;
        }

        return true;
    }

    return false;
}

---->[View#performClick]----------------------
public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

复制代码

番外:ViewGroup对子View的添加

---->[ViewGroup对子View的添加]---------------------------
private View[] mChildren;
mChildren = new View[ARRAY_INITIAL_CAPACITY];//默认12

---->[ViewGroup#addInArray]---------------------------
private void addInArray(View child, int index) {
    View[] children = mChildren;
    final int count = mChildrenCount;
    final int size = children.length;
    if (index == count) {
        if (size == count) {
            mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
            System.arraycopy(children, 0, mChildren, 0, size);
            children = mChildren;
        }
--->    children[mChildrenCount++] = child;
    } else if (index < count) {
        if (size == count) {
            mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
            System.arraycopy(children, 0, mChildren, 0, index);
            System.arraycopy(children, index, mChildren, index + 1, count - index);
            children = mChildren;
        } else {
            System.arraycopy(children, index, children, index + 1, count - index);
        }
--->    children[index] = child;
        mChildrenCount++;
        if (mLastTouchDownIndex >= index) {
            mLastTouchDownIndex++;
        }
    } else {
        throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
    }
}

|--- 关于ViewGroup添加View,追踪了一下:
addView(一参)-->addView(两参)-->addView(三参)-->addViewInner-->addInArray

复制代码

小结:

总的来说源码看下来,感觉view事件分发机制也并不像我想像中的那么难

在自定义View中至多也就是ViewGroup+子View的触摸事件协调,Activity一般不参和

Activity的事件分发实质上是DecorView的事件分发,所以都是View家的,Activity打了波酱油

最后我想强调一下 d10to18t 返回false时的不同点,这也是我以前比较迷惑的:见下图

Android事件分发机制[-View-] 源码级
Android事件分发机制[-View-] 源码级

后记:捷文规范

1.本文成长记录及勘误表

项目源码 日期 附录
V0.1-- 2018-2-21

发布名: Android事件分发机制[源码级]
捷文链接: juejin.im/post/5c6b71…

2.更多关于我

笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的github: github.com/toly1994328

我的简书: www.jianshu.com/u/e4e52c116…

我的掘金: juejin.im/user/5b42c0…

个人网站:www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持

Android事件分发机制[-View-] 源码级

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web 2.0 Architectures

Web 2.0 Architectures

Duane Nickull、Dion Hinchcliffe、James Governor / O'Reilly / 2009 / USD 34.99

The "Web 2.0" phenomena has become more pervasive than ever before. It is impacting the very fabric of our society and presents opportunities for those with knowledge. The individuals who understand t......一起来看看 《Web 2.0 Architectures》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具