事件分发学习中的疑问与解答

事件分发学习中的疑问与解答

2018, May 31    
  1. 当一个viewgroup中有重叠的view时,是如何决事件处理的?
    在viewgroup遍历子view时,是通过一个list去遍历,然后分别判断每个view是否包含当前event中的坐标点来决定是否有接收事件的资格。
    判断范围,可以看出,在判断点是否在组件内时并不会考虑同级view的遮盖,只考虑父组件的位置,因此,能够判断分发顺序只需要考虑在list中的顺序,在顺序前的先被执行。在生成list时,是通过 buildOrderedChildList方法构建了一个先序列表,方法的注释是如下,大概意思就是按照z轴排序,然后再按照控件的绘制顺序,也就是添加到group的顺序。在遍历的时候反向遍历,从最后一个开始遍历,因此z最大或者最后添加的接受事件的优先级更高
	/**
     * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
     * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
     * after use to avoid leaking child Views.
     *
     * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
     * children.
     */

*注释
transformPointToViewLocal 的作用是将坐标的起始位置移动到child的起始位置去判断,可以理解为 point[0] - child.mleft x和child的x相减,因为mleft为与父组件的相对位置,需要考虑父组件内容的移动,因此再加上父组件的移动距离(移动为内容的整体移动,与父组件的相对位置是不变的),也就是 mScrollX。
pointInView 的作用是判断是否在当前的view中,就是判断计算后的x,y是否在view的宽高范围内。(slot目前的使用应该是touch处理的扩展范围)

protected boolean isTransformedTouchPointInView(float x, float y, View child,
        PointF outLocalPoint) {
    final float[] point = getTempPoint();
    point[0] = x;
    point[1] = y;
    transformPointToViewLocal(point, child);
    final boolean isInView = child.pointInView(point[0], point[1]);
    if (isInView && outLocalPoint != null) {
        outLocalPoint.set(point[0], point[1]);
    }
    return isInView;
}
   
public void transformPointToViewLocal(float[] point, View child) {
    point[0] += mScrollX - child.mLeft;
    point[1] += mScrollY - child.mTop;
    if (!child.hasIdentityMatrix()) {
        child.getInverseMatrix().mapPoints(point);
    }
}
    
public boolean pointInView(float localX, float localY, float slop) {
    return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
            localY < ((mBottom - mTop) + slop);
}

2- moveevent与滑动 3- activity,viewgroup,view的事件分发中的一些细节 通常说道事件分发都涉及三个方法:dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent,但是在每一个层级的事件处理都设计这几个方法,而且中间的过程中也会涉及其他常见的对象。 (1) onInterceptTouchEvent在ViewGroup中的,view不涉及拦截事件 (2) 在viewgroup中若拦截事件,会直接调用onTouchEvent方法;而在view中,会先去调用onTouchListener中的onTouch方法,如果onTouch方法返回了false才回执行onTouchEvent方法,否则不会执行。 (3) dispatchTouchEvent方法会返回是否消耗掉,view的dispatchTouchEvent直接被viewGroup返回,所以这个方法的的意义是只是针对activity,如果dispatchTouchEvent中一直不处理,activity的onTouchEvent就会处理(activity – window –viewgroup – view) (4) viewGroup中 onTouchEvent 和 子view的dispatchTouchEvent的执行是互斥的,不可能即处理了onTouchEvent方法又传给了子view,所以如何即让子view去执行又能让group去执行。默认情况下viewgroup的onInterceptTouchEvent是返回false的。