欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android滑动触摸事件

程序员文章站 2022-07-03 09:10:44
参考博客: https://juejin.im/post/5c3c8538f265da6142741d63...

参考博客:即于下面博客演变出自己的理解

     https://juejin.im/post/5c3c8538f265da6142741d63

      https://blog.csdn.net/xmxkf/article/details/79481243

郭霖

    https://blog.csdn.net/guolin_blog/article/details/9097463

   https://blog.csdn.net/guolin_blog/article/details/9153747

   事件分发机制:整体上遵循递归原则,递是指事件分发传递从Activity---->DecorView ----->ViewGroup---->View

  归是指:事件的消费从 View---->ViewGroup---->DecorView---->Activity

  要素,连接关系,运行环境如下图

 android滑动触摸事件

分析 :

 1: 方法要素:dispatchTouchEvent是在View中定义的,ViewGroup重写了它。并且View 中的dispatchTouchEvent函数是交给

        OnTouchListener处理的,并调用onTouchEvent ----->这是都是View类型控件自己处理

       ViewGroup类控件,先是判断是否拦截或者是否禁止拦截这两个变量,来决定是否交给子控件处理,然后才自己处理,自己处理的逻辑和View处理的 逻辑是一样的

2:  要素说明 :

    onInterceptTouchEvent(MotionEvent ev) ----》如果返回true,那么子类就没有机会获的事件

   onTouchListener.onTouch ----->监听touch事件,onTouch返回值决定是否消费此事件,是否回调onTouchEvent

  onTouchEvent ------>控件解析处理事件

  setOnClickListener ----->View中的函数,设置点击事件回调onClick函数

3: 关键函数,参数解析

     disallowIntercept /  requestDisallowIntercept 变量,是否请求父控件禁止拦截事件,默认是false ---->即 默认是拦截的

     interceptTouchEvent() -----> 1: 一般在滑动冲突的时候,我们复写这个函数,然后在函数里面加上一个判断条件,什么条件下返回true 拦截事件,什么情况下返回false 不拦截事件。2:系统对于这个函数的处理事:判断 用户的触摸事件是在父控件下的子控件范围内,如果在就返回false 否则返回true ,那要是不在子控件范围内(用户就是点击 activity空白处)或者子控件没有消费事件(dispatchTouchEvent() 返回false )那么interceptTouchEvent()还是返回false, 继续执行super.dispatchTouchEvent 但是这个已经是ViewGroup自己的事件执行了

    android滑动触摸事件

4: View对 dispatchTouchEvent函数的调用 处理关系如果返回true (不管是子类返回还是回调父控件返回),事件都是在这一层结束

       1: 判断控件enable是否可用 && 是否设置onTouchListener监听 调用onTouch函数

     2: onTouch函数:如果返回 false ,说明没有被消费,继续调用 onTouchEvent, 返回true 直接给dispatchTouchEvent返回true

     事件在View这一层消费

android滑动触摸事件

    android滑动触摸事件

     5:View对onTouchEvent函数分析

      该方法会处理一系列的 action_down  action_move  action_up 事件(按下,滑动,抬起),并在 up事件的时候会回调 performClick() 函数,并且回调OnClickListener.onClick()方法 

伪代码分析:

       1: ViewGroup 的 dispatchTouchEvent函数 

android滑动触摸事件

public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    final float xf = ev.getX();
    final float yf = ev.getY();
    final float scrolledXFloat = xf + mScrollX;
    final float scrolledYFloat = yf + mScrollY;
    final Rect frame = mTempRect;
    boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (action == MotionEvent.ACTION_DOWN) {
        if (mMotionTarget != null) {
            mMotionTarget = null;
        }
        if (disallowIntercept || !onInterceptTouchEvent(ev)) {
            ev.setAction(MotionEvent.ACTION_DOWN);
            final int scrolledXInt = (int) scrolledXFloat;
            final int scrolledYInt = (int) scrolledYFloat;
            final View[] children = mChildren;
            final int count = mChildrenCount;
            for (int i = count - 1; i >= 0; i--) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                        || child.getAnimation() != null) {
                    child.getHitRect(frame);
                    if (frame.contains(scrolledXInt, scrolledYInt)) {
                        final float xc = scrolledXFloat - child.mLeft;
                        final float yc = scrolledYFloat - child.mTop;
                        ev.setLocation(xc, yc);
                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                        if (child.dispatchTouchEvent(ev))  {
                            mMotionTarget = child;
                            return true;
                        }
                    }
                }
            }
        }
    }
    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
            (action == MotionEvent.ACTION_CANCEL);
    if (isUpOrCancel) {
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }
    final View target = mMotionTarget;
    if (target == null) {
        ev.setLocation(xf, yf);
        if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
            ev.setAction(MotionEvent.ACTION_CANCEL);
            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        }
        return super.dispatchTouchEvent(ev);
    }
    if (!disallowIntercept && onInterceptTouchEvent(ev)) {
        final float xc = scrolledXFloat - (float) target.mLeft;
        final float yc = scrolledYFloat - (float) target.mTop;
        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        ev.setAction(MotionEvent.ACTION_CANCEL);
        ev.setLocation(xc, yc);
        if (!target.dispatchTouchEvent(ev)) {
        }
        mMotionTarget = null;
        return true;
    }
    if (isUpOrCancel) {
        mMotionTarget = null;
    }
    final float xc = scrolledXFloat - (float) target.mLeft;
    final float yc = scrolledYFloat - (float) target.mTop;
    ev.setLocation(xc, yc);
    if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
        ev.setAction(MotionEvent.ACTION_CANCEL);
        target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        mMotionTarget = null;
    }
    return target.dispatchTouchEvent(ev);

}

 

2: View的 dispatchTouchEvent函数 

android滑动触摸事件

总结  

   1: android触摸事件是先从 ViewGroup 在传递给View

   2:  ViewGroup 的dispatchTouchEvent 函数 会 根据 disallowIntercept 来决定是否分发给子控件,此变量的作用就是在 interceptTouchEvent 返回true 之后(不允许事件向子类传递)子类还是想收到 事件这个问题。

   3: 子类如果消费事件了,就会通过 dispatchTouchEvent函数传递消费事件的返回值true

  4:  View 的dispatchTouchEvent  会判断 控件是否设置 enable  && onTouchListener 来决定是否走onTouch函数,onTouch函数的返回false, 又会走 onTouchEvent 函数。最终将事件的消费情况传递给 父类

应用:

   说了这么都,实际的应用了 ?

  1: 一般如果自定义控件继承ViewGroup ,那么需要重写 onInterceptTouchEvent() 函数,并根据自己的的业务逻辑灵活的返回true 或者false  -------》比如在ViewPager 中嵌套 ListView 或者RecyclerView 就要控制好 ViewPager 的 onInterceptTouchEvent()函数是否拦截事件了 ,一般都是定义一个 规则,超过某段距离认为用户想横向滑动,这个时候就拦截事件不向RecyclerView 分发,没超过一定距离就 认为用户想垂直滑动,这个时候事件件不拦截事件需要向RecyclerView 分发

 

本文地址:https://blog.csdn.net/u013620306/article/details/107471236