当前位置:网站首页>根据热门面试题分析Android事件分发机制(二)---事件冲突分析处理
根据热门面试题分析Android事件分发机制(二)---事件冲突分析处理
2022-07-07 07:03:00 【光阴剑客】
(一)事件冲突概述
事件冲突一般发生在父view和子view的组合中,比如说viewpager和listview 的组合中。产生事件冲突的原因是一个事件(down,up,move)到来时,有时候我们希望是A view处理,但是却是B view处理。这样就导致了我们想处理事件的view收不到事件,不想处理事件的view收到了事件。而解决冲突就是我们通过viewGroup的onInterceptTouchEvent()方法去合理的分发事件,让想处理事件的view收到事件,不想处理的view躺平啥也不做。
(二)没有事件冲突的效果展示
(看看美女,再写代码~~)
言归正传,如上图所示,demo是由一个viewpager+listView的组合,我们发现在没有事件冲突的情况下,应该是既能左右滑动,也能上下滑动。为了演示,我们分别继承自viewpager和Listview,viewPager作为父view,重写它的onInterceptTouchEvent方法,listview作为子view,重写dispatchTouchevent.(需要的时候),demo的源码很简单,我会在文章末尾给出。
(说明:如果不重写onInterceptTouchEvent和listview的dispatchTouchevent方法,我们直接使用viewpager+listview组合会发现不会产生事件冲突,那是因为sdk里面已经解决了冲突,可以查看viewpager的源码)
模拟冲突的产生 :
情景1: 当我们在父view,也就是viewPager的onInterceptTouchEvent方法中直接返回true,拦截事件,这时候我们会发现我们只可以响应view pager的左右滑动,而不能响应listview的上下滑动,原因就是我们在父view直接拦截了事件,导致子view listview 收不到事件,无法响应事件。
情景2: 当我们在父view,也就是viewPager的onInterceptTouchEvent方法中直接返回false,不拦截事件,我们这时会发现,viewpager 不可以左右滑动,只可以上下滑动listview,这是因为viewpager不拦截事件,将事件分发给了子view处理,所以他无法响应自己的左右滑动事件。
(三)面试题:你遇到过滑动冲突吗?你是怎么处理的?
解析: 这时假如你自己遇到过,就答你遇到的冲突产生的情景,然后说下你的解决方法,如果你没有遇到过,就回答咱们本文中提到的viewpager+listview的场景。根据上面的内容总结下就可以,然后就是如何解决冲突的回答,这时候你可以回答有两种冲突解决方法:
方法一:在父view的onInterceptTouchEvent中解决冲突,代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//在父view中进行冲突处理,不需要子view实现dispatchTouchEvent方法
float x = ev.getX();//记录下x,y的初始值
float y = ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
super.onInterceptTouchEvent(ev);
//这里需要调用sdk中的viewpager中的
//onInterceptTouchEvent方法处理一些特殊问题
//不调用的话我们拦截了事件也无法响应滑动
return false;//不能拦截down事件,拦截了down
//事件,就无法收到后面的move,和up事件了
case MotionEvent.ACTION_MOVE:
float xDiff = Math.abs(x-mLastX);
//当move事件来的时候我们去计算本次的x值和上次的
//x值,得到一个差值
float yDiff = Math.abs(y-mLastY);
//y值的计算和x一样
if(xDiff > yDiff){
//x的差值大于y的差值,表示当前是左右滑动。此时应该把事件交给viewpager处理,拦截事件
isIntercept = true;
break;
}
if(yDiff > xDiff){
//y值大于x值,表示当前是上下滑动,此时应该把事件交给listview处理,不拦截事件
isIntercept = false;
break;
}
// break;
}
mLastX = x;//更新上一次的x值和y值
mLastY = y;
return isIntercept;
}
方法2:在子view中做滑动冲突处理,listview中的代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float x = ev.getX();
float y = ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
//请求父view不拦截事件
break;
case MotionEvent.ACTION_MOVE:
float xDiff = Math.abs(x-mLastX);
float yDiff = Math.abs(y-mLastY);
if(xDiff > yDiff){
//当前是左右滑动,自己不需要处理事件,请求父view拦截事件。
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:break;
}
mLastX = x;
mLastY = y;
//调用父类的dispatchTouchEvent处理事件
return super.dispatchTouchEvent(ev);
}
viewPager中的代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//在子view中进行冲突处理.
if(ev.getAction() == MotionEvent.ACTION_DOWN){
super.onInterceptTouchEvent(ev);
//必须返回false,否则子view无法收到事件,此处会有一个面试题
return false;
}
//返回true,是为了当子view请求父view拦截事件的时候,好拦截事件
return true;
}
在子view中处理冲突和在父view中处理冲突的原理差不多,都是通过拦截和不拦截的方式将事件分给需要的view。回答的时候就把两种方法大致描述一下。
然后接着,面试官可能会问你:为啥不能拦截down事件,拦截了down事件,后面的move,up事件还能收到吗?
这两个问题其实互为因果,也就是拦截了down事件,后面的move,up事件都无法收到了。这时候你就可以开始将你看的事件分发机制的源码摆出来了:
...省略了部分代码
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//如果子view没有调用requestDisallowInterceptTouchEvent方法设置标志的话,这个地方会为false,因为在前面down事件来的时候会重置一下标志
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);//若是拦截了down事件,这个地方会返回true;无法进入到接下来的事件分发逻辑,然后mFirstTouchTarget就不会被赋值,无法走入到当前的代码逻辑中,导致后面来的move和up事件直接会被拦截
=============插播片段==============
if (!canceled && !intercepted) {
....
// 事件分发处理逻辑
.....
}
===========================
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
//如果没有处理down事件的时候,move和up事件都会被拦截
intercepted = true;
}
...省略了部分代码
具体的事件分发流程,可以看我上一篇博客根据热门面试题分析Android事件分发机制(一)
(四)总结:
其实事件分发机制和滑动冲突只要理解了源码都不难解决,我的建议就是要看博客然后自己去撸源码,写demo模拟每一种场景,这时你就会发现这些原理都是通的,在面试的时候也能帮你拿到主动权,大多数时候我们面试的时候感觉自己都被面试官气势给压住,其实都是我们技术了解得不深的原因。当我们技术了解得够透彻的时候,你去面试的时候会发现,面试官会被你虐的。那种感觉特别爽
(五)demo地址
这个源码我是放到gitee上的,已经申请了开放权限,还在审批,如果急须源码的伙伴,可以留个邮箱,我私发给你们~~~~
事件冲突演示demo地址
边栏推荐
- asp. How to call vb DLL function in net project
- Jenkins+ant+jmeter use
- Pycharm create a new file and add author information
- thinkphp数据库的增删改查
- 印象笔记终于支持默认markdown预览模式
- js逆向教程第二发-猿人学第一题
- Unity shader (to achieve a simple material effect with adjustable color attributes only)
- 信息安全实验四:Ip包监视程序实现
- CMD startup software passes in parameters with spaces
- VSCode+mingw64+cmake
猜你喜欢
Where is the answer? action config/Interceptor/class/servlet
Pytest installation (command line installation)
[SVN] what is SVN? How do you use it?
How to use clipboard JS library implements copy and cut function
Information Security Experiment 3: the use of PGP email encryption software
Jenkins+ant+jmeter use
Lesson 1: finding the minimum of a matrix
Mysql database transaction learning notes
Lecture 1: stack containing min function
Difference between interface iterator and iteratable
随机推荐
STM32 and motor development (from stand-alone version to Networking)
Mysql database lock learning notes
Network request process
How will fashion brands enter the meta universe?
ComputeShader
NATAPP内网穿透
Pycharm importing third-party libraries
数据建模中利用3σ剔除异常值进行数据清洗
Jenkins task grouping
Variable parameter of variable length function
How to use clipboard JS library implements copy and cut function
**grafana安装**
La différence entre viewpager 2 et viewpager et la mise en œuvre de la rotation viewpager 2
How to speed up video playback in browser
Interface test API case, data and interface separation
Kubernetes cluster capacity expansion to add node nodes
[4G/5G/6G专题基础-147]: 6G总体愿景与潜在关键技术白皮书解读-2-6G发展的宏观驱动力
SAP MM STO单据的外向交货单创建后新加ITEM?
网易云微信小程序
Unity3d interface is embedded in WPF interface (mouse and keyboard can respond normally)