View事件分发(三)-ViewGroup事件分发(源码分析)

2020-10-30  本文已影响0人  如愿以偿丶

1.ViewGroup的事件分发三个重要方法

1.1.dispatchTouchEvent
1.2.onInterceptTouchEvent
1.3.onTouchEvent

2.通过上述三个方法阅读源码

 1.dispatchTouchEvent源码

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            //当手指按下ACTION_DOWN
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                ...
                //1.主要是将清除mFirstTouchTarget置为空  mFirstTouchTarget = null
                cancelAndClearTouchTargets(ev);
            }

            //检查是否拦截(默认false)
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                //请求父容器不拦截,默认false
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    //2.调用onInterceptTouchEvent();
                    intercepted = onInterceptTouchEvent(ev);
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }

            // 检查是否为取消  false
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
            // 声明newTouchTarget
            TouchTarget newTouchTarget = null;
            // canceled和intercepted都为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) {
                        final View[] children = mChildren;
                        //倒序的一个循环,应该是处理RelativeLayout,从上倒下获取每一个View
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            ...
                            newTouchTarget = getTouchTarget(child);
                            ...
                            //3.重要方法,该方法会调用子View的dispatchTouchEvent或者父类的dispatchTouchEvent
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                ...
                                //添加touchTarget,并给mFirstTouchTarget也赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                break;
                            }
                        }
                    }
                }
            }
        return handled;
    }
   1.1 cancelAndClearTouchTargets(ev);源码
    /**
     * Cancels and clears all touch targets.
     */
    private void cancelAndClearTouchTargets(MotionEvent event) {
        if (mFirstTouchTarget != null) {
           ...
            //清除TouchTargets
            clearTouchTargets();
        }
    }

    /**
     * Clears all touch targets.
     */
    private void clearTouchTargets() {
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
            do {
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
             //将mFirstTouchTarget置为空
            mFirstTouchTarget = null;
        }
    }
  1.2.onInterceptTouchEvent源码
  //默认返回false
  public boolean onInterceptTouchEvent(MotionEvent ev) {
         ...
        return false;
    }
  1.3.dispatchTransformedTouchEvent源码
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {

        final boolean handled;
            if (child == null) {
                //如果没有子View,调用父类的dispatchTouchEvent(event);,就回到了上一节讲的View的事件分发
                handled = super.dispatchTouchEvent(event);
            } else {
                //否则调用子View的dispatchTouchEvent(event);,后续就是View的事件分发。此时就走了一遍了。
                handled = child.dispatchTouchEvent(event);
            }
            return handled;
        }
       ...
        transformedEvent.recycle();
        return handled;
    }

由源码可知:

1.ViewGroup的dispatchTouchEvent,经过一系列判断,当手指按下,首先调用onInterceptTouchEvent,onInterceptTouchEvent默认返回false不拦截,在通过for循环遍历child,如果child为空,调用View的dispatchTouchEvent,否则,调用子View的dispatchTouchEvent。之后就回到了View的事件分发。

通过代码测试

1.自定义ViewGroup

//自定义ViewGroup
public class TouchViewGroup extends LinearLayout {

    public TouchViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //事件分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("TAG","ViewGroup  dispatchTouchEvent----"+ev.getAction());
        return super.dispatchTouchEvent(ev);
    }

    //事件拦截
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("TAG","ViewGroup  onInterceptTouchEvent----"+ev.getAction());
        return super.onInterceptTouchEvent(ev);
    }

    //事件触摸
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG","ViewGroup  onTouchEvent----"+event.getAction());
        return super.onTouchEvent(event);
    }

2.自定义View

public class TouchView extends View {

    public TouchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("TAG","View  dispatchTouchEvent----"+event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG","View   onTouchEvent----"+event.getAction());
       return super.onTouchEvent(event);
    }
}

3.布局文件

<com.yc.drawdashboard.touch.TouchViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".twelve.ScalableActivity">

        <com.yc.drawdashboard.touch.TouchView
            android:id="@+id/touchview"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@color/colorPrimary"/>

</com.yc.drawdashboard.touch.TouchViewGroup>

4.主页面代码,添加onTouchListener,onClickListener

public class ScalableActivity extends AppCompatActivity {

    private TouchView mTouchView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scalable);

        mTouchView = findViewById(R.id.touchview);

        mTouchView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("TAG","View   onTouch----"+event.getAction());
                return false;
            }
        });

        mTouchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("TAG","View   onClick");
                Toast.makeText(ScalableActivity.this,"onClick",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

通过手指点击屏幕打印执行顺序

第一种情况:

当ViewGroup和View的重写方法都返回默认值时。

执行顺序:

ViewGroup   dispatchTouchEvent按下
ViewGroup   onInterceptTouchEvent按下
View   dispatchTouchEvent按下
View   onTouch按下
View   onTouchEvent按下
ViewGroup   dispatchTouchEvent移动
ViewGroup   onInterceptTouchEvent移动
View   dispatchTouchEvent移动
View   onTouch移动
View   onTouchEvent移动
ViewGroup   dispatchTouchEvent抬起
ViewGroup   onInterceptTouchEvent抬起
View   dispatchTouchEvent抬起
View   onTouch抬起
View   onTouchEvent抬起
onClick

动画效果:

image_one.gif

第二种情况:

当View的onTouchEvent返回true时,其他都不变

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG","View  onTouchEvent----"+event.getAction());
        return true;
    }

执行顺序:

ViewGroup   dispatchTouchEvent按下
ViewGroup   onInterceptTouchEvent按下
View   dispatchTouchEvent按下
View   onTouch按下
View   onTouchEvent按下
ViewGroup   dispatchTouchEvent移动
ViewGroup   onInterceptTouchEvent移动
View   dispatchTouchEvent移动
View   onTouch移动
View   onTouchEvent移动
ViewGroup   dispatchTouchEvent抬起
ViewGroup   onInterceptTouchEvent抬起
View   dispatchTouchEvent抬起
View   onTouch抬起
View   onTouchEvent抬起
此时不调用onClick,因为View的onTouchEvent没有调用super.onTouchEvent(event)

动画效果:

image_two.gif

第三种情况:

将View的onClickListener去掉,其他都不变
执行顺序:

ViewGroup   dispatchTouchEvent按下
ViewGroup   onInterceptTouchEvent按下
View   dispatchTouchEvent按下
View   onTouch按下
View   onTouchEvent按下

动画效果:

image_three.gif

第四种情况:

将ViewGroup的onInterceptTouchEvent返回true,其他都不变

    //事件拦截
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("TAG","ViewGroup  onInterceptTouchEvent----"+ev.getAction());
        return true;
    }

执行顺序:

ViewGroup   dispatchTouchEvent按下
ViewGroup   onInterceptTouchEvent按下
ViewGroup   onTouchEvent按下

动画效果:

image_four.gif
上一篇下一篇

猜你喜欢

热点阅读