ViewDragHelper的点击事件处理
在上一篇ViewDragHelper的介绍后,已经完成了自定义控件SwipeLayout的滑动,这一篇,我们来处理它的点击事件。之前提到过,它有两个子view,最开始显示的是surfaceLayout,隐藏在右边的是bottomLayout。当你给surfaceLayout设置点击事件时,你会发现,surface确实可以点击,但向左滑动却什么反应都没有,还是在响应点击事件。原因是什么呢,通过对事件分发的认识,可以得出结论:由于给surfaceLayout设置了点击事件,导致这个子view的变成可点击的了,于是你手指的down事件从SwipeLayout向子view分发时,SwipeLayout并没有拦截这个down,而surfaceLayout作为ViewGroup,默认也不拦截,但是surface已经没有子view在给它分发了,它拦不拦截,这个down都得归它处理,由于surface设置了点击事件,所以它能消费了这个down事件。这么一来,SwipeLayout 如果不在拦截方法里做些处理,后面纷至而来的move事件,都会传递surface,那么SwipeLayout的onTouchEvent就怎么都不会调用,肯定也就滑不起来了。那为什么surface没设置点击事件时,swipeLayout可以滑动呢,这是因为surface不设置点击事件,那它就不会消耗down事件的,那么这个down事件会以冒泡的形式,再向swipeLayout传递,而swipeLayout的onTouchEvent始终返回true,所以swipe消耗了down,之后的move便会一直传递给它。
照这样看来,得down者,得move!但我们的点击事件是必须要设置的,这样就导致,surface一定会先于父view消耗这个down,好,down给surface没关系,但move事件父View表示我要抢过来。要不然因为你的点击导致我都滑不起来,我还有毛用。由于我们把SwipeLayout的拦截事件方法全部交给了viewDragHelper的shouldInterceptTouEvent ()这个方法,那么看看它什么情况下会拦截事件。

一顿switch判断后,最终会判断mDragState是不是等于拖动。那好说,我们就看这个变量在哪里被赋值。经过一番搜索,可以找到以下几个调用关系:

-------------------------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------------

从下往上看的话,你会发现在viewDragHelper的拦截方法中,当你一步步走完tryCaptureViewForDrag时,mDragState会被赋值等于STATE_DRAGGING。根据前面提过,子view是一定要消耗down的,接下来的move,从外往内传递,而且子view也没有设置不允许父view拦截的标志,那么父view的拦截方法就会一直调用,也就是shouldInterceptTouchEvent会一直调用。上面action =move是一直能进来的,那tryCaptureViewForDrag为啥不走呢,原因很明了了:就是前面的pastSlop一直等于false嘛!从pastSlop 的赋值情况来看,它的结果跟checkTouchSlop有直接的关系:接着看图:

在上一篇博客中,我们重写了mCallback的几个回调,但是并没有重写getViewHorizontalDragRange这个回调,看来有的回调不重写就会留坑。默认这个方法返回0,所以checkTouSlop也就返回false,pastSlope返回false,后面的方法不执行,move动作始终不被swipeLayout拦截,也就滑不起来。那我重写这个回调,让它返回值大于0,是不是就行了呢,当然可以!当checkHorzontal为true,系统会判断你手指从点击到滑动的这段距离是否超过系统认为的最小距离,如果超过,系统认为你是在滑动,那么后面发送是连锁反应导致swipeLayout拦截下move事和up事件,并在processTouchEvent里处理,这个方法根据不同的action调用callback,例如move会回调水平移动,以及位置改变,up会回调onViewReleased。下面是我重写的getViewHorizontalDragRange这个回调,
@Override
public int getViewHorizontalDragRange(@NonNull View child) {
return helper.getTouchSlop();
}
helper.getTouSlop的值就是上图中的mTouchSlop。这个值默认等于8,它还与动画的执行时间有关系,最好别设得太大。到这里,在运行下程序,surface的点击以及swipeLayout的滑动都可以了,但你滑着会发现还有一些问题,当swipelayout处于关闭状态,你向右滑会触发surface的点击事件,为什么呢,难道我右滑时,swipeLayout没有拦截成功这个move吗?当你这么猜想时,你可以在swipeLayout的拦截方法里打个日志,记录下
shouldInterceptTouchEvent的返回结果,你会发现,你的猜想就是对的!看来,有些回调你就算实现了,坑还是会有的。我们在分析下拦截里的move到了干了什么好事:

就上面的4个圈,给我们挖的坑,第一个圈里面,由于我们重写了上面的那个回调,现在它的返回值是true,没问题,第二个圈在前一篇博客里,记不记得我们在这个回调里设置了边界,当开始处于关闭状态,你向右滑,我们做了限制,让surface返回还是0.所以newLeft=0,第3个圈是刚重写的,它的值等于大于0,没问题,出问题的是,我们的newLeft = oldLeft,oldLeft是你点击view的左上角顶点的横坐标,它就是等于0的,所以,在这里直接break了。你看

圈里面提前break,跳出循环,导致tryCaptureViewForDrag 也不会执行,这样move、up事件还是会下发给surface的。这个问题怎么解决呢?其实很简单,你只要强制把返回结果改为true,后面的事交给processTouchEvent去搞定就完了。因为它就是负责将你的手势转为相应的回调去操作子view,拦截只是个不让你进入美丽花园的巨人,童话故事就是这么说的。这是修改后的拦截代码
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = helper.shouldInterceptTouchEvent(ev);
float curX = ev.getX();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = curX;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(curX - startX) > helper.getTouchSlop()){
result = true;
}
break;
} return result;
}
第一行代码可以保证你把系统默认的拦截都走一遍,后面的就是判断用户滑动的距离超过了系统认定的最小滑动距离,我们就拦截下来。就这么简单的处理,你在向右滑,就还是被swipeLayout拦截,子view的点击事件就不会生效,你单纯去点击,当然也不会进入我们的判断,swipeLayout就正常的分发。至此,点击事件的坑就踩到这里了。看到这里,你会发现,废话那么多,才加了10行左右的代码,看别人的博客的话,别人直接告诉你,重写某某回调,点击事件就有了,那当然很省事,但这个探索的过程,我认为是很值得的,因为我经常闲的蛋疼。
ViewDragHelper的点击事件处理的更多相关文章
- UITableViewCell上的按钮点击事件处理
转自: http://www.aichengxu.com/view/42871 UITableViewCell上的按钮点击事件处理,有需要的朋友可以参考下. 今天突然做项目的时候,又遇到处理自定义的 ...
- Android零基础入门第66节:RecyclerView点击事件处理
前面两期学习了RecyclerView的简单使用,并为其item添加了分割线.在实际运用中,无论是List还是Grid效果,基本都会伴随着一些点击操作,那么本期就来一起学习RecyclerView的点 ...
- 在多行列表中id同名的<a>标签点击事件处理方法
Struts2标签 问题描述:最近项目中在使用struts迭代标签动态生成列表的过程中,由于每一行li元素中包含<a>超链接,如下图 一开始超链接是这样的<a href=“#” id ...
- Android学习笔记(1):常用按钮点击事件处理方式
1.从布局文件获取对应的控件然后对其添加点击监听器. Button loginBtn; @Override protected void onCreate(Bundle savedInstanceSt ...
- iOS中按钮点击事件处理方式
写在前面 在iOS开发中,时常会用到按钮,通过按钮的点击来完成界面的跳转等功能.按钮事件的实现方式有多种,其中 较为常用的是目标-动作对模式.但这种方式使得view与controller之间的耦合程度 ...
- 点击事件处理, 以及hitTest:withEvent:实现
发送触摸事件后, 系统会将事件添加到系统UIApplication的事件管理队列中 UIApplication会在事件队列的最前端取出事件,然后分发下去,以便处理, 通常会把事件首先分发给KeyWin ...
- jquery TypeError: $(...).live is not a functio,动态添加class的点击事件处理
jq版本更新后无live函数的处理.TypeError: $(...).live is not a function jquery live函数语法 jquery版本更新, 发现一个问题: jq自带的 ...
- iOS 子视图超出父视图范围点击事件处理!
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ UIView *view = [super hitTest:point ...
- iOS UITextView点击事件处理
自定义一个UITextView UITextView 的selectedRange 影响 selectedTextRange 改变前者可影响后者 self.selectedRange -->se ...
随机推荐
- effective-java学习笔记---静态工厂方法替代构造方法
使用静态方法的优点: 1.它们是有名字的,生成的客户端代码更易阅读. 如:返回素数的静态方法 BigInteger.probablePrime 2.与构造方法不同,它们不需要每次调用时都创建一个对象. ...
- java——构造器理解
构造器理解 什么是构造器 构造器也叫构造方法:用于对象的初始化: 写构造器注意事项 构造器名与类名一致:有返回值但是不能定义返回类型(返回值类型是本类,可以加一个空的return): 构造器的调用 通 ...
- Python python 五种数据类型--数字
一:Number 数字 不可变类型 整型(Int) - 通常被称为是整型或整数,是正或负整数,不带小数点. 长整型(long integers) - 无限大小的整数,整数最后是一个大写或小写的L.(P ...
- 图的广度优先遍历(bfs)
广度优先遍历: 1.将起点s 放入队列Q(访问) 2.只要Q不为空,就循环执行下列处理 (1)从Q取出顶点u 进行访问(访问结束) (2)将与u 相邻的未访问顶点v 放入Q, 同时将d[v]更新为d[ ...
- 读者来信 | 设置HBase TTL必须先disable表吗?(已解决)
今日有朋友加好友与我探讨一些问题,我觉得这些问题倒挺有价值的:于是就想在本公众号开设一个问答专栏,方便技术交流与分享,专栏名就定为:<读者来信>.如遇到本人能力有限难以解决的问题,该贴将会 ...
- Python学习前端之JavaScript
JavaScript介绍 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中),后将其改名ScriptEase(客户端执行的语言). Nets ...
- POJ 3273Monthly Expense(二分答案)
题目链接 思路如下 题意:这一题让我们在一个 n 个数的序列,分成连续的的 m个子串(一个数也可是一个子串),是在所有子串中 和最大的子串 的和最小. 思路:我们可以用 二分法 来一个一个枚举答案,二 ...
- 让你第一次认识到Java的内存管理
发现之前写的可读性不好,这次准备试试换风格,去掉长篇大论,觉得这个风格好的,麻烦点个赞啦 清理.JVM的妙处 大家以后都是程序员,假设你很不幸,需要自己交钱租房子. 你作为一个小穷人,租的房子到期了( ...
- SpringCloud(二)之我学 Ribbon
1.负载均衡 Ribbon 虽然不是显示的配置为一个子项目,但是无论是在 API 网关的转发请求,还是服务之间的调用 Feign ,都是通过 Ribbon 来做负载均衡的. 负载均衡,主要是为了对系统 ...
- MySQL入门,第一部分,全局管理命令
1.连接数据库命令 mysql -h localhost -u root -p 回车后输入密码即可连接到数据库 2.显示当前mysql管理系统中的所有数据库 SHOW DATABASES; 3.显示当 ...