事件分发机制与滑动冲突

版权声明:转载请说明出处:http://blog.csdn.net/yhaolpz https://blog.csdn.net/yhaolpz/article/details/61638424

事件分发机制

事件分发过程主要由dispatchTouchEventonInterceptTouchEventonTouchEvent 三个方法共同完成:

  1. public boolean dispatchTouchEvent(MotionEvent ev)

    用来进行事件分发,如果事件能够传递给当前view,则此方法一定调用,返回结果受当前view的onTouchEvent和下级view的dispatchTouchEvent方法影响,表示是否消耗了当前事件

  2. public boolean onInterceptTouchEvent(MotionEvent event)

    在dispatchTouchEvent内部调用,用来判断是否拦截某个事件

  3. public boolean onTouchEvent(MotionEvent event)

    在dispatchTouchEvent内部调用,用来处理点击事件,返回结果表示是否消耗了当前事件,如果不消耗则在同一事件序列中,当前view无法再次接受到事件

点击事件达到顶级View(一般为ViewGroup)后,会调用viewGroup的dispatchTouchEvent方法,如果顶级ViewGroup的onInterceptTouchEvent返回true,则事件由ViewGroup处理,这时如果ViewGroup的mOnTouchListener被设置,则onTouch会被调用,onTouch若返回true,ViewGroup的onTouchEvent不会再被调用;若设置了mOnClickListener,则onClick会被调用。如果顶层ViewGroup不拦截事件即onInterceptTouchEvent返回false,则事件传递给子View,这时子View的dispatchTouchEvent会被调用,到此为止,事件已经从顶级View传递了下一层View,如此循环,完成整个事件的分发。

如果一个View的onTouchEvent返回false,那么它的父容器的onTouchEvent将会被调用,表示无法消耗(解决)事件,需要交给上级。

ViewGroup默认不拦截任何事件,即Android源码中ViewGroup的onInterceptTouchEvent默认返回false

View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。

  • 伪代码助记:
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean result= false;
    if(onInterceptTouchEvent(ev)){
         result = onTouchEvent(ev);
    }else{
        result = child.dispatchTouchEvent(ev);
    }
    return result;
}
  • 一张图说明事件分发机制:

这里写图片描述

滑动冲突

在界面中内外两层同时可以滑动,就产生了滑动冲突,如何解决滑动冲突呢?其实这是一件非常简单的事,因为滑动冲突的解决有固定的套路,只要知道了这个固定套路,问题就十分容易解决了。

常见的滑动冲突场景可分为以下三种:

  1. 外部滑动方向和内部滑动方向不一致

    ViewPager和Fragment组成的页面中包括一个ListView,这种情况是有滑动冲突的,但是ViewPager内部处理了这种滑动冲突,因此采用ViewPager时无须关注这个问题,如果采用的不是ViewPager而是ScrollView,那就必须处理滑动冲突了,否则内外两层就只有一层能滑动

  2. 外部滑动方向和内部滑动方向一致

    内外两层在同一个方向都可以滑动,系统无法知道用户到底想让哪一层滑动

  3. 上面两种情况的嵌套

    比如SlideMenu、ScrollView和ListView三者同时出现

处理规则

场景1:根据滑动是水平滑动还是竖直滑动来判断到底由谁来拦截事件

场景2:虽然无法根据滑动方向来判断,但是这时一般在业务上有状态可以依赖,比如业务上有规定,当处于某种状态时需要外部View响应用户滑动,而处于另一种状态时需要内部View响应滑动

场景3:其实很简单,结合场景1和场景2的处理规则,就能处理场景3的滑动冲突

解决方式

1.外部拦截法
点击事件先经过父容器的拦截处理,如果父容器需要响应就拦截,不需要就不拦截,拦截处理需要写在父容器的onInterceptTouchEvent方法里,至于原因可以回顾 彻底理解view事件分发机制,这是解决滑动冲突的必要基础

2.内部拦截法
父容器不拦截任何事件,子view需要响应就直接消耗,否则就交由父容器处理

这就是Android 滑动冲突的所有姿势~ 是不是非常简单?只要你真的理解View事件分发机制,那滑动冲突就像纸老虎一样一捅就破~

完~

参考书籍《Android开发艺术探索》

猜你喜欢

转载自blog.csdn.net/yhaolpz/article/details/61638424