目录

AndroidTouch分发机制续篇


图解刨之
sequenceDiagram participant Activity participant PhoneWindow participant DecorView participant ViewGroup participant Child Activity -->> Activity :1 、boolean dispatchTouchEvent(MotionEvent ev)->解析点1 Activity ->> PhoneWindow :2、getWindow().superDispatchTouchEvent(ev)->解析点2 PhoneWindow ->> DecorView :3、 return this.mDecor.superDispatchTouchEvent(event)->解析点3 DecorView ->> ViewGroup : 4、return super.dispatchTouchEvent(event)->解析点4 ViewGroup -->> ViewGroup:第1步:开始处理时间分发onFilterTouchEventForSecurity(ev) ViewGroup -->> ViewGroup:第2步:检测是否需要清空目标和状态 ViewGroup -->> ViewGroup:第3步:检查child是否允许parent可以拦截 requestDisallowInterceptTouchEvent() ViewGroup -->> ViewGroup:第4步:如果child允许parent可以拦截,就会调用onInterceptTouchEvent()询问parent是否要拦截 ViewGroup -->> ViewGroup:第5步:如果没有child可响应,则分发给自己,有则分发给child,通过调用dispatchTransformedTouchEvent是否传递有第三个参数进行区分 ViewGroup -->> dispatchTouchEvent:第6步:调用dispatchTouchEvent(event) dispatchTouchEvent -->> dispatchTouchEvent:先调用onTouch事件 dispatchTouchEvent -->> dispatchTouchEvent:onTouch返回false时继续调用onTouchEvent(event) ViewGroup -->> ViewGroup:第7步:再次检查取消标记,并进行相应的处理

总结:

  • 1、首先Activity接收到touch事件后在dispatchTouchEvent方法中会调用DecorView的superDispatchTouchEvent方法;
  • 2、DecorView最终调用的是viewGroup的3、dispatchTouchEvent方法;
  • 3、在viewgroup的dispatchTouchEvent方法中会做判断:
    • 1)检查disallowIntercept
    • 2)disallowIntercept允许拦截:检查onInterceptTouchEvent是否真的要拦截,如果拦截则event不再分发下去,调用自己的dispatchTouchEvent方法,如果不拦截,则调用ViewGroup自己dispatchTransformedTouchEvent方法,
    • 3)在dispatchTransformedTouchEvent里又做了两件事,如果有childview,就调用childview的dispatchTouchEvent方法将事件分发下去,如果没有childview,仍然是调用自己的dispatchTouchEvent方法。
    • 4)所以调用view的dispatchTouchEvent有两种情况,一种是拦截了,不传给子view了,一种是没有子view了;
解析:
  • 解析点1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public boolean dispatchTouchEvent(MotionEvent ev) {

    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    //先将事件分发给window,如果windown消费就直接return true结束本次touch事件
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    //window未消费时走onTouchEvent事件
    return onTouchEvent(ev);
}
  • 小结:代码比较短,但重点肯定在getWindow().superDispatchTouchEvent(ev),不然如果走onTouchEvent,之后也就没啥好说的了,我们移步到getWindow().superDispatchTouchEvent(ev),getWindow其实就是PhoneWindow的实例

  • 解析点2

1
2
3
4
5
6
7
8
public boolean superDispatchTouchEvent(MotionEvent event) {

    //方法很简单,里面调用的是mDecor的superDispatchTouchEvent
    //DecorView是PhoneWindow的一个final的内部类并且继承FrameLayout的,
    //也是Window界面的最顶层的View对象,其实decorView是在activity的setContentView方法里面构建的

    return this.mDecor.superDispatchTouchEvent(event);
}
  • 解析点3
1
2
3
4
5
6
7
//DecorView类的定义
private class DecorView extends FrameLayout implements RootViewSurfaceTaker {}

public boolean superDispatchTouchEvent(MotionEvent event) {
            //其调用了parent的方法,也就是Framelayout的方法,而FrameLayout并没有重写该方法,而FraneLayout的parent是ViewGroup,故直奔ViewGroup
            return super.dispatchTouchEvent(event);
        }
  • 解析点4,由解析点3可知,实际是调用viewGroup的,代码较长,只保留重要代码
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
public boolean dispatchTouchEvent(MotionEvent ev) {

    //调试用,不理会
    if (this.mInputEventConsistencyVerifier != null) {
        this.mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }

    if (ev.isTargetAccessibilityFocus() && this.isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }

    //第1步:开始处理时间分发
    //onFilterTouchEventForSecurity()表示是否要分发该触摸事件。 
    //如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。
    //否则,则对触摸事件进行分发,即返回true。
    boolean handled = false;
    if (this.onFilterTouchEventForSecurity(ev)) {
        int action = ev.getAction();
        //取低8位 该值为触摸类型,是down 还是move 或者up之类的
        int actionMasked = action & MotionEvent.ACTION_MASK;

        // 第2步:检测是否需要清空目标和状态
        // 如果是ACTION_DOWN(即按下事件),则清空之前的触摸事件处理目标和状态。
        // 这里的情况状态包括:
        // (01) 清空mFirstTouchTarget链表,并设置mFirstTouchTarget为null。
        //      mFirstTouchTarget是"接受触摸事件的View"所组成的单链表
        // (02) 清空mGroupFlags的FLAG_DISALLOW_INTERCEPT标记
        //      如果设置了FLAG_DISALLOW_INTERCEPT,则不允许ViewGroup对触摸事件进行拦截。
        // (03) 清空mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVEN标记

        if (actionMasked == 0) {
            this.cancelAndClearTouchTargets(ev);
            this.resetTouchState();
        }

        // 第3步:检查当前ViewGroup是否想要拦截触摸事件
        // 
        // 是的话,设置intercepted为true;否则intercepted为false。
        // 如果不是"按下事件(ACTION_DOWN)" 且 mFirstTouchTarget为null;设置intercepted为true
        // 否则的话,就执行if代码块里面的内容。
        boolean intercepted;
        boolean canceled;
        if (actionMasked != 0 && this.mFirstTouchTarget == null) {
            intercepted = true;
        } else {

            // 检查禁止拦截标记:FLAG_DISALLOW_INTERCEPT
            // 如果调用了requestDisallowInterceptTouchEvent()标记的话,则FLAG_DISALLOW_INTERCEPT会为true。
            // 例如,ViewPager在处理触摸事件的时候,就会调用requestDisallowInterceptTouchEvent()
            //     ,禁止它的父类对触摸事件进行拦截

            canceled = (this.mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!canceled) {
                // 如果禁止拦截标记为false的话,则调用onInterceptTouchEvent();并返回拦截状态。   
                intercepted = this.onInterceptTouchEvent(ev);
                ev.setAction(action);
            } else {
                intercepted = false;
            }
        }

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

        // 第4步:检查当前的触摸事件是否被取消
        // 
        // (01) 对于ACTION_DOWN而言,mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVENT位肯定是0;因此,canceled=false。
        // (02) 当前的View或ViewGroup要被从父View中detach时,PFLAG_CANCEL_NEXT_UP_EVENT就会被设为true;
        //      此时,它就不再接受触摸事情。
        canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;

        // 第5步:将触摸事件分发给"当前ViewGroup的子View和子ViewGroup"
        // 
        // 如果触摸"没有被取消",同时也"没有被拦截"的话,则将触摸事件分发给它的子View和子ViewGroup。  
        // 如果当前ViewGroup的孩子有接受触摸事件的话,则将该孩子添加到mFirstTouchTarget链表中。

        boolean split = (this.mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        ViewGroup.TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        int idBitsToRemove;
        if (!canceled && !intercepted) {
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? this.findChildWithAccessibilityFocus() : null;
            if (actionMasked == 0 || split && actionMasked == 5 || actionMasked == 7) {

                // 这是获取触摸事件的序号 以及 触摸事件的id信息。
                // (01) 对于ACTION_DOWN,actionIndex肯定是0
                // (02) 而getPointerId()是获取的该触摸事件的id,并将该id信息保存到idBitsToAssign中。
                //    这个触摸事件的id是为多指触摸而添加的;对于单指触摸,getActionIndex()返回的肯定是0;
                //    而对于多指触摸,第一个手指的id是0,第二个手指的id是1,第三个手指的id是2,...依次类推。
                idBitsToRemove = ev.getActionIndex();
                int idBitsToAssign = split ? 1 << ev.getPointerId(idBitsToRemove) : -1;

                // 清空这个手指之前的TouchTarget链表。
                // 一个TouchTarget,相当于一个可以被触摸的对象;它中记录了接受触摸事件的View
                this.removePointersFromTouchTargets(idBitsToAssign);
                int childrenCount = this.mChildrenCount;

                // 获取该ViewGroup包含的View和ViewGroup的数目,
                // 然后递归遍历ViewGroup的孩子,对触摸事件进行分发。
                // 递归遍历ViewGroup的孩子:是指对于当前ViewGroup的所有孩子,都会逐个遍历,并分发触摸事件;
                //   对于逐个遍历到的每一个孩子,若该孩子是ViewGroup类型的话,则会递归到调用该孩子的孩子,...
                if (newTouchTarget == null && childrenCount != 0) {
                    float x = ev.getX(idBitsToRemove);
                    float y = ev.getY(idBitsToRemove);
                    ArrayList<View> preorderedList = this.buildOrderedChildList();
                    boolean customOrder = preorderedList == null && this.isChildrenDrawingOrderEnabled();
                    View[] children = this.mChildren;

                    for(int i = childrenCount - 1; i >= 0; --i) {
                        int childIndex = customOrder ? this.getChildDrawingOrder(childrenCount, i) : i;
                        View child = preorderedList == null ? children[childIndex] : (View)preorderedList.get(childIndex);

                        // 如果child可以接受触摸事件,
                        // 并且触摸坐标(x,y)在child的可视范围之内的话;
                        // 则继续往下执行。否则,调用continue。
                        // child可接受触摸事件:是指child的是可见的(VISIBLE);或者虽然不可见,但是位于动画状态。
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }

                            childWithAccessibilityFocus = null;
                            i = childrenCount - 1;
                        }

                        if (canViewReceivePointerEvents(child) && this.isTransformedTouchPointInView(x, y, child, (PointF)null)) {

                            // getTouchTarget()的作用是查找child是否存在于mFirstTouchTarget的单链表中。
                            // 是的话,返回对应的TouchTarget对象;否则,返回null。
                            newTouchTarget = this.getTouchTarget(child);
                            if (newTouchTarget != null) {
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            // 重置child的mPrivateFlags变量中的PFLAG_CANCEL_NEXT_UP_EVENT位。
                            resetCancelNextUpFlag(child);

                            // 调用dispatchTransformedTouchEvent()将触摸事件分发给child。
                            if (this.dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                                // 如果child能够接受该触摸事件,即child消费或者拦截了该触摸事件的话;
                                // 则调用addTouchTarget()将child添加到mFirstTouchTarget链表的表头,并返回表头对应的TouchTarget
                                // 同时还设置alreadyDispatchedToNewTouchTarget为true。
                                this.mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    for(int j = 0; j < childrenCount; ++j) {
                                        if (children[childIndex] == this.mChildren[j]) {
                                            this.mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    this.mLastTouchDownIndex = childIndex;
                                }

                                this.mLastTouchDownX = ev.getX();
                                this.mLastTouchDownY = ev.getY();
                                newTouchTarget = this.addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            ev.setTargetAccessibilityFocus(false);
                        } else {
                            ev.setTargetAccessibilityFocus(false);
                        }
                    }

                    if (preorderedList != null) {
                        preorderedList.clear();
                    }
                }

                // 如果newTouchTarget为null,并且mFirstTouchTarget不为null;
                // 则设置newTouchTarget为mFirstTouchTarget链表中第一个不为空的节点。
                if (newTouchTarget == null && this.mFirstTouchTarget != null) {
                    for(newTouchTarget = this.mFirstTouchTarget; newTouchTarget.next != null; newTouchTarget = newTouchTarget.next) {
                        ;
                    }

                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }

        // 第6步:进一步的对触摸事件进行分发
        // 
        // (01) 如果mFirstTouchTarget为null,意味着还没有任何View来接受该触摸事件;
        //   此时,将当前ViewGroup看作一个View;
        //   将会调用"当前的ViewGroup的父类View的dispatchTouchEvent()"对触摸事件进行分发处理。
        //   即,会将触摸事件交给当前ViewGroup的onTouch(), onTouchEvent()进行处理。
        // (02) 如果mFirstTouchTarget不为null,意味着有ViewGroup的子View或子ViewGroup中,
        //   有可以接受触摸事件的。那么,就将触摸事件分发给这些可以接受触摸事件的子View或子ViewGroup。
        if (this.mFirstTouchTarget == null) {

            // 注意:这里的第3个参数是null
            handled = this.dispatchTransformedTouchEvent(ev, canceled, (View)null, -1);
        } else {
            ViewGroup.TouchTarget predecessor = null;
            ViewGroup.TouchTarget target = this.mFirstTouchTarget;

            label163:
            while(true) {
                while(true) {
                    if (target == null) {
                        break label163;
                    }

                    ViewGroup.TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                        if (this.dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                            handled = true;
                        }

                        if (cancelChild) {
                            if (predecessor == null) {
                                this.mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }

                            target.recycle();
                            target = next;
                            continue;
                        }
                    }

                    predecessor = target;
                    target = next;
                }
            }
        }

        // 第7步:再次检查取消标记,并进行相应的处理
        if (!canceled && actionMasked != 1 && actionMasked != 7) {
            if (split && actionMasked == 6) {
                int actionIndex = ev.getActionIndex();
                idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                this.removePointersFromTouchTargets(idBitsToRemove);
            }
        } else {
            this.resetTouchState();
        }
    }

    if (!handled && this.mInputEventConsistencyVerifier != null) {
        this.mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    }

    return handled;
}