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

向那些曾经无法跨越的鸿沟敬上----吾王已至


事件分发.png

开篇先明确几点
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)  ---我想这样应该表述的淋漓尽致了

图例.png


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

0.自定义两个测试View

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

测试布局.png

/**
 * 作者:张风捷特烈<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之外点击

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

点击Activity.png

点空白处.png

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里

点击ViewGroup_.png

点击ViewGroup.png

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里, 这里感觉像是...一根链条。万一哪块掉链子了会怎么样?

点击子View.png

点击子View_.png

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单体,事件也传不下去了。

掉链子.png

---->[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

ViewGroup掉链子.png

---->[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掉链子

现在回到初始情况

View掉链子.png

---->[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可以决定事件是否打断

打断.png

---->[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消费事件,事件就不会往下传递了

当ViewGroup消费事件.png

---->[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消费事件

子View消费.png

---->[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

dispatchTouchEvent.png

---->[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时的不同点,这也是我以前比较迷惑的:见下图

打断.png

ViewGroup掉链子.png


最后用一个小例子来表达一下通过o18t来控制事件响应行为

如果父View左滑了,那么就不截断子View的滑动(孩子消费) 如果父View右滑了,那么就截断子View的滑动(自己消费)

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/2/21/021:9:06<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:事件测试ViewGroup
 * 左滑动  ViewGroup 响应
 * 右滑动  View单体 响应
 */
public class FatherViewGroup extends FrameLayout {
    private static final String TAG = "EventTest";
    boolean isLeft = false;
    
    public FatherViewGroup(Context context) {
        super(context);
    }
    public FatherViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        setBackgroundColor(0xff9869B7);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return isLeft;
    }
    
    float x;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                x = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                float curX = event.getX();
                float dx = curX - this.x;
                isLeft = dx < 0;
                x = curX;
                setBackgroundColor(ColUtils.randomColor());
                break;
        }
        return true;
    }
}

/**
 * 作者:张风捷特烈<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);
    }
    float x;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        setBackgroundColor(ColUtils.randomColor());
        return true;
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券