美文网首页
源码解析View的事件分发

源码解析View的事件分发

作者: 撸码的皮大叔 | 来源:发表于2019-03-12 19:20 被阅读0次

当我们点击屏幕,就会产生了点击事件,这个事件封装成了一个类:MotionEvent
当MotionEvent产生后,那么系统就会讲MotionEvent传递给View的层级,MotionEvent在View中的层级传递过程就是点击事件分发。他有3个重要的方法

  • dispatchTouchEvent(MotionEvent event) -用来进行事件的分发
  • onInterceptTouchEvent(MotionEvent event) - 用来进行事件的拦截,在dispatchTouchEvent()中调用,注意的是View没有提供该方法
  • onTouchEvent(MotionEvent event) - 用来处理点击事件,在dispatchTouchEvent()方法中进行调用

1.事件分发机制传递过程

当MotionEvent产生后->Activity.dispatchTouchEvent()->PhoneWindow->DecorView->ViewGroup

从Activity的dispatchTouchEvent()开始分析:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
   // 当所有view的onTouchEvent都返回ture ,整个事件结束,否则返回Activity的onTouchEvent()
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

在看getWindow().superDispatchTouchEvent(ev)看看getWindow是什么?

   public Window getWindow() {
        return mWindow;
    }

继续看Window里面,发现Window是个抽象类,superDispatchTouchEvent这也是个抽象方法,找他实现类PhoneWindow。

  @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

重大发现来了一个DecorView,有人会问DecorView是什么?https://www.jianshu.com/p/324603bb5791

 public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

DecorView继承FrameLayout,FrameLayout又是继承ViewGroup,所有最终事件调用顶层View(ViewGroup)的dispatchTouchEvent方法。

2.View的事件分发源码解析

先看一段代码TouchView.class

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

 @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
}

MainActivity.class

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View view = findViewById(R.id.touch_view);

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

打印结果:

03-11 20:06:58.562 3105-3105/com.lorenzogao.view E/TAG: onTouch------>0
03-11 20:06:58.562 3105-3105/com.lorenzogao.view E/TAG: onTouchEvent---->0
03-11 20:06:58.629 3105-3105/com.lorenzogao.view E/TAG: onTouch------>1
03-11 20:06:58.629 3105-3105/com.lorenzogao.view E/TAG: onTouchEvent---->1
03-11 20:06:58.632 3105-3105/com.lorenzogao.view E/TAG: onClick

场景一: 这个方法执行顺序是 OnTouchListener()->onTouchEvent()->OnClickListener();

如果OnTouchListener的onTouch()返回true时
打印结果

03-11 20:12:14.425 3105-3105/com.lorenzogao.view E/TAG: onTouch------>0
03-11 20:12:14.491 3105-3105/com.lorenzogao.view E/TAG: onTouch------>1

场景二:当OnTouchListener的onTouch()返回true时,只执行OnTouchListener,其他都不执行了。

场景三:当onTouchEvent的返回true时,不会执行onClick

onClick在View里面执行,没调用super.onTouchEvent所以调不到PerformClick()

  • dispatchTouchEvent() 用来处理事件分发的
 public boolean dispatchTouchEvent(MotionEvent event) {
      ...

        boolean result = false;

            //ListenerInfo 存放了关于View的所有listener,如 OnClickListener、OnLongClickListener等。
            ListenerInfo li = mListenerInfo;

  //如果mOnTouchListener不为null并且onTouch方法返回true,则表示事件被消费了,就不会往下执行了。
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED//是否是ENABLED 可用的
                    && li.mOnTouchListener.onTouch(this, event)) {//如果是false   result = false 则反之到这里验证了场景1
                result = true;
            }

       //如果result=false 就会执行onTouchEvent事件,则反之,到这里验证了场景2
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        return result;
    }

目前还没看到点击事件

  • onTouchEvent() 用来处理点击事件
public boolean onTouchEvent(MotionEvent event) {
     ...
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
    ...
                            if (!focusTaken) {
                            //PerformClick 点击事件,验证了场景1
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClickInternal();
                                }
                            }
                        ...
        return false;
    }

3.ViewGroup事件分发机制源码分析

3.1.调用方法顺序, 前提是子view设置了setOnTouchListener要不然就不会调用onTouch,设置onClick要不然不会调用onClick。

  • 第一次DOWN
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent
  • 第二次MOVE
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent
  • 第三次UP
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent->View.onClick

3.2. 去掉onClick事件(没有的一个地方消费事件,没有一个地方返回true,所以走了一遍)

  • UP事件

ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent->ViewGroup.onTouchEvent

3.3 在View的onTouchEvent方法返回true

  • 第一次DOWN
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent
  • 第二次MOVE
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent
  • 第三次UP
    ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent

3.4 在ViewGroup的onInterceptTouchEvent方法返回true

ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> ViewGroup.onTouchEvent

dispatchTouchEvent方法分析

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
      
       ...

        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
          
...
            if (actionMasked == MotionEvent.ACTION_DOWN) {
              //清除所有的mFirstTouchTarget
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            //是否拦截
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                 //正常情况下会调用onInterceptTouchEvent(ev)
                    intercepted = onInterceptTouchEvent(ev);//默认情况下返回false
                    ev.setAction(action); 
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }

            TouchTarget newTouchTarget = null;
           // 默认情况下可以执行
            if (!canceled && !intercepted) {

                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
              
                    if (newTouchTarget == null && childrenCount != 0) {
                  ···
           
                        final View[] children = mChildren;
                       //反序列for循环,因为RelativeLayout布局可以叠加
                        for (int i = childrenCount - 1; i >= 0; i--) {

                           ...
                  
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                            
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                          //获取子View
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                              ...
                               
                         
                               //如果返回true进来,添加一个target,addTouchTarget方法主要给mFirstTouchTarget 赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                           
                                break;
                        }
                  
                    }
                }
            }

            if (mFirstTouchTarget == null) {

//dispatchTransformedTouchEvent方法 child是个null,所有会调用super.dispatchTouchEvent(event)。
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {  
                   ...
            }
        return handled;
    }

cancelAndClearTouchTargets方法

private void cancelAndClearTouchTargets(MotionEvent event) {
        if (mFirstTouchTarget != null) {
       ...
          //清除所有的mFirstTouchTarget
            clearTouchTargets();
        }
    }

clearTouchTargets方法 清除所有的mFirstTouchTarget

    private void clearTouchTargets() {
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
            do {
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
            mFirstTouchTarget = null;
        }
    }

dispatchTransformedTouchEvent方法

 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
         //child==null 会调用自己super.dispatchTouchEvent(event)    
   if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
   //child!=null 会调用自己child.dispatchTouchEvent (event)  
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
···
        return handled;
    }

在ViewGroup里子View没有一个地方消费事件(返回true),只会进来一次只响应DOWN事件,如果你响应MOVE 、UP事件,必须找个地方返回true,对于ViewGroup想拦截子View的触摸事件,覆写onInterceptTouchEvent方法返回true,如果onInterceptTouchEvent方法返回true,执行自己的onTouchEvent(),在ViewGroup里子View没消费事件也会调用自己onTouchEven()。

相关文章

网友评论

      本文标题:源码解析View的事件分发

      本文链接:https://www.haomeiwen.com/subject/ybxbwftx.html