通过《Android事件分发(一)》我们了解了Android的事件分发机制,不熟悉的,可以回头再去看一遍。

有了这方面的知识基础,我们来解决实际研发的过程中,老生常谈的事件冲突问题。

解决这类问题,其实是有方法的。下面先介绍这两种方法,然后再结合以上三种问题,分别进行讲解

方法一:外部拦截法

顾名思义,外部拦截法,就是指在外部进行拦截,让事件不传递下去。其实就是对外部的dispatchTouchEvent和onInterceptTouchEvent动手脚

方法二:内部拦截法

事件冲突大致分为三种

1.方向一致

我们用ScrollView嵌套ListView,同为上下方向的滑动。

我们假设一个需求:

1.listView向下滑时,如果还没有滑到listView的头部的话,listView继续滑动;否则ScrollView滑动。

2.listView向上滑时,如果还没有滑到listView的底部的话,listView继续滑动;否则ScrollView滑动。

<com.example.weights.CustomScrollViewandroid:id="@+id/scrollView"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="100dp"android:background="#0000ff"android:text="头部" /><ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#0000ff"app:adapter="@{da}" /><TextViewandroid:layout_width="match_parent"android:layout_height="100dp"android:background="#0000ff"android:text="尾部" />

1.1 外部拦截法

重写ScrollView的onInterceptTouchEvent

int tempY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {boolean intercept = false;//表示是否拦截int y = (int) ev.getY();switch (ev.getAction()){case MotionEvent.ACTION_DOWN:tempY = y;//记录按下时的Y坐标intercept = super.onInterceptTouchEvent(ev);//这里返回的是false。不然,不会调用其他Action方法break;case MotionEvent.ACTION_MOVE:if(null != listView){if(listView.getFirstVisiblePosition() == 0 && y > tempY){//list显示第一个,而且是向下滑intercept = true;//ScrollView拦截break;}else if(listView.getLastVisiblePosition() == listView.getCount() - 1 && y < nowY){//list显示最后一个,而且是向上滑intercept = true;break;}intercept = false;}break;case MotionEvent.ACTION_UP:intercept = false;break;default:break;}return intercept;
}

所谓外部拦截法,其实就是外部父容器做处理。在这里,当父容器ScrollView拦截事件后,就会调用父容器ScrollView自己的onTouchEvent,即ScrollView处理滚动,否则交给ListView消费。

2.1 内部拦截法

int nowY;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {//内部拦截法int y = (int) ev.getY();switch (ev.getAction()){case MotionEvent.ACTION_DOWN:nowY = y;getParent().requestDisallowInterceptTouchEvent(true);//禁止父容器拦截事件break;case MotionEvent.ACTION_MOVE:View topView = getChildAt(0);View bottomView = getChildAt(getChildCount() - 1);if(y > nowY && getFirstVisiblePosition() == 0 && null != topView && topView.getTop() == 0){//往下滑,并且是第一项,父容器拦截getParent().requestDisallowInterceptTouchEvent(false);}else if(y < nowY && getLastVisiblePosition() == getCount() - 1 && null != bottomView && bottomView.getBottom() == getHeight()){//往上滑,并且是第后一项,父容器拦截getParent().requestDisallowInterceptTouchEvent(false);}break;}return super.dispatchTouchEvent(ev);
}

所谓内部拦截法,其实就是在内部子容器里,通过requestDisallowInterceptTouchEvent方法,控制父容器是否拦截事件。requestDisallowInterceptTouchEvent是ViewParent接口的一个抽象方法,实现是在ViewGroup里面

@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {// We're already in this state, assume our ancestors are tooreturn;}if (disallowIntercept) {mGroupFlags |= FLAG_DISALLOW_INTERCEPT;//注意这里} else {mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;}// Pass it up to our parentif (mParent != null) {mParent.requestDisallowInterceptTouchEvent(disallowIntercept);}
}

注意mGroupFlags这个变量,回想下,在ViewGroup的dispatchTouchEvent方法里面有这么一段代码

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {//允许拦截intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed
} else {intercepted = false;
}

这里说明一点,在使用内部拦截法的时候,需要重写父容器的onInterceptTouchEvent,

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {if(ev.getAction() == MotionEvent.ACTION_DOWN){//这里需要返回false,因为如果这里返回true,事件就不会往下传递,导致子View无法接收事件return false;}else{//其他事件拦截。已配合子容器调用requestDisallowInterceptTouchEvent使用return true;}
}

这里的ScrollView没有重写,是因为ScrollView本身已经重写了onInterceptTouchEvent方法

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {...final int action = ev.getAction();if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {return true;}if (super.onInterceptTouchEvent(ev)) {return true;}
...return mIsBeingDragged;
}

2.方向不一致

典型的例子,就是ViewPager和ListView。不过,ViewPager已经做了处理,有兴趣的可以看看ViewPager的源码。

解决套路还是外部拦截和内部拦截,在判断条件上,需要做些调整。归根结底还是根据X和Y的滑动距离来判断是否消费事件。

3.多层嵌套,方向不一致

解决套路还是外部拦截和内部拦截,根据需要,把之拆开成以上两种情况,然后解决,这里就不多做介绍了,有兴趣的朋友,可以自行尝试。


胖子总结

  • 自己写个demo,试一试,比看十遍文章都有用
  • 温馨提示:点成线,线成面,切勿贪心,否则一脸懵逼
  • 胖子有什么理解错误的,欢迎大家指出来,一起讨论、学习、进步
  • 期待胖子为了巩固、加深基础的《Java虚拟机(JVM)》

Androi事件分发( 二),解决事件冲突相关推荐

  1. Android 系统(218)---Android的事件分发机制以及滑动冲突的解决

    Android的事件分发机制以及滑动冲突的解决 声明:  本文主要涉及VIew的事件分发与滑动冲突的解决,关于View的事件分发流程的部分内容参考自:  Android事件分发机制详解:史上最全面.最 ...

  2. 自定义View(二)--表层浅析View的事件分发机制和滑动冲突

    转载请注明出处:From李诗雨:http://blog.csdn.net/cjm2484836553/article/details/54387722 不诗意的女程序猿不是好厨师~ 这篇文章来得有些曲 ...

  3. 【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  4. 【Android View事件分发机制】滑动冲突

    View内容滑动概念 scrollTo scrollBy scrollTo(x,y) x,y 是绝对值,如果x,y不变,重复调用是不会移动的. scrollBy(x,y) x,y是增量之,每次调用都会 ...

  5. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  6. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  7. javaScript事件(二)事件处理程序

    一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 前面提到,事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字.响应 ...

  8. button添加插槽之后绑定不来事件_javaScript基础事件(二)事件处理程序

    前面提到,事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字.响应某个事件的函数就叫事件处理程序(也叫事件处理函数.事件句柄).事件处理程序的名字以&quo ...

  9. java 事件分发线程_事件分发线程EDT

    所有的事件处理都在Event Dispatch Thread(EDT)上进行,此一类事件模型通常叫做单线程模型. 这种模型规定所有对组件的访问操作必须在EDT 上完成. 为什么对于组件的访问需要在ED ...

  10. java 事件分发线程_Java事件调度线程说明

    我最近开始学习和探索Java中GUI编程的基础知识. 经过一段时间的编程,我只完成了后端工作或其他工作,因此,我最接近用户界面的是命令控制台(令人尴尬的是,我知道). 我正在使用Swing,据我所知, ...

最新文章

  1. AI+DevOps正当时
  2. Spartan-6系列内部模块介绍之可配置逻辑模块(CLB)
  3. 支持Windows 7的CAD—AutoCAD Civil 3D 2010
  4. boost::contract模块实现check宏的测试程序
  5. hibernate的映射关系配置及对会话工厂的初始化。以及struts2写实例查询
  6. wxpython实现简单图书管理系统
  7. 使用PostBackUrl属性实现跨页面传值
  8. redhat6.3下配置使用cenos yum源
  9. 发布一个域安全级别的无代码InfoPath表单作为文档库模版 (InfoPath 一)
  10. Windows 下修改 MySQL 编码为 utf8
  11. 1705. 吃苹果的最大数目
  12. ESXi配置vCenter服务器
  13. ai人工智能的数据服务_可解释的AI-它对数据科学家有何影响?
  14. Excel-VBA 快速上手(一、宏、VBA、过程、类型与变量、函数)
  15. iMac电脑启动ideal跑Java项目报错(Class JavaLaunchHelper is implemented in both...One of the two will be used.)
  16. 使用EditPlus 3时,如何重新设置HTML Page的Default模板
  17. Failed installing 'Tomcat9' service
  18. 速达3000pro saas数据库修复
  19. 图像宽度所占字节必须是4的倍数详解
  20. 特征值问题编程基础:特征值特征向量的求解和性质

热门文章

  1. 问题 H: A+B 输入输出练习VIII
  2. 阿里云飞天系统质效管理体系入选信通院“软件质效领航者”优秀案例
  3. PostgreSQL 磁盘空间的保护伞 PG_repack VS 表膨胀
  4. html都有哪些事件,HTML有哪些事件属性?
  5. 价值3888开源企业发卡网源码/全网对接/全新UI风格/完美运营
  6. python练习题:程序员问卷调查
  7. 条件概率下的全概率公式
  8. 双代号网络图如何用计算机画,怎么画双代号网络图,双代号网络图的绘制规则和步骤...
  9. python华表_鹤归华表 丁令威化鹤
  10. 原创分享 | 如何从非技术层面实现数据驱动