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 ...
随机推荐
- C# 基础知识系列- 6 Lambda表达式和Linq简单介绍
前言 C#的lambda和Linq可以说是一大亮点,C#的Lambda无处不在,Linq在数据查询上也有着举足轻重的地位. 那么什么是Linq呢,Linq是 Language Intergrated ...
- OpenCV-Python 轮廓特征 | 二十二
目标 在本文中,我们将学习 如何找到轮廓的不同特征,例如面积,周长,质心,边界框等. 您将看到大量与轮廓有关的功能. 1. 特征矩 特征矩可以帮助您计算一些特征,例如物体的质心,物体的面积等.请查看特 ...
- nodejs 配置代理服务器
const express = require('express'); const proxy = require('http-proxy-middleware'); const cors = req ...
- Swagger2 初始用
1.结合Spring-Boot 引入 pom 依赖 <dependency> <groupId>io.springfox</groupId> <artifa ...
- 微信阻止ios下拉回弹,橡皮筋效果
直接阻止touchmove事件就好了(需设置passive: false): document.addEventListener("touchmove", function(evt ...
- iOS 内置图片瘦身
一.iOS 内置资源的集中方式 1.1 将图片存放在 bundle 这是一种很常见的方式,项目中各类文件分类放在各个 bundle 下,项目既整洁又能达到隔离资源的目的.采用 bundle 的加载方式 ...
- (3)SQL Server表分区
1.简介 当一个表数据量很大时候,很自然我们就会想到将表拆分成很多小表,在执行查询时候就到各个小表去查,最后汇总数据集返回给调用者加快查询速度.比如电商平台订单表,库存表,由于长年累月读写较多,积累数 ...
- Error while processing transaction.java.lang.IllegalStateException: begin() called when transaction is OPEN!
Spark Streaming从flume 中使用Poll拉取数据时,报如下错误: Error while processing transaction. java.lang.IllegalState ...
- Jmeter 压力测试笔记(3)--脚本调试/签名/cookie/提升吞吐量/降低异常率/提升单机并发性能
import XXXsign.Openapi2sign;---导入jar包中的签名方法 String str1 = "12121"; ---需要被签名的字段:向开发了解需要哪些哪些 ...
- css3新的选择器
CSS3新的选择器 ele[att^="val"] /*属性att的值以val开头的元素*/ ele[att$="val"] /*属性att的值以val结尾的元 ...