问题需求
原先效果是用户在ViewPager
上长按会弹出一个dialog
,现在加了一个需求:用户有时候在长按时出现手指向左或向右微小滑动,此时也需要判断为长按并弹出dialog
初步分析
外边的ViewPager
是可以左右滑动的,现在希望单个界面即子view
接收到触摸事件后能接管本次事件序列中后续事件,即子View
接收到触摸事件后,注意此时手指未松开,所以还处于一次完整的事件序列中,等待一段时间Android
默认是400ms
后会识别为长按事件,此时子View
的长按事件被触发。那么问题来了,怎么才能在子view
接收到触摸事件并能接管本次事件序列呢?
这就要说到老生常谈的事件拦截了,正常拦截事件有外部拦截和内部拦截两种,这里因为代码中子view已写好现成的手势判断的相关方法,故这里为简单起见,采用内部拦截法来实现。
修改方法LauncherPagerAdapter.java
1 | private static final int mTouchSlop = 40; |
这样的话,再对子view
添加长按事件处理,也能顺利的接收到长按事件了。效果上便达到了微小长按滑动也会进入长按事件的处理逻辑中,注意此时微小的向左或向右滑动,viewpager
本身并不会移动了,修改之前Viewpager
会跟着滑动。
这个问题里用的是内部拦截法,其实也可以自定义Viewpager
中重写canScrool()
方法,这里就不介绍了,相比而言稍稍麻烦了一点
下面顺便说下滑动冲突的一般解决思路,这是个老生常谈的话题,解决方法已形成固定的流程,需要根据具体的实际业务需求灵活改变。
先介绍几个事件分发处理的几个常见方法dispatchTouchEvent
主要是用来分发事件onInterceptTouchEvent
主要是用来拦截事件的(ViewGroup才有这个方法,View没有这个方法)onTouchEvent
这个方法主要是用来处理事件的requestDisallowInterceptTouchEvent(true)
这个方法能够影响父View是否拦截事件,true 表示父 View 不拦截事件,false 表示父 View 拦截事件
滑动冲突一般解决方法
1.外部拦截法
一般是通过重写父控件的onInterceptTouchEvent
方法,然后根据具体的需求,来决定父控件是否拦截事件。如果拦截返回返回true,不拦截返回false,比如说希望上下滑动不要拦截,那么就在父控件的onInterceptTouchEvent
方法里的MotionEvent.ACTION_MOVE
这个case块里返回false就好。如果希望指定位置不拦截,则再进行位置判断即可。
对于外部拦截法我们的核心工作一般是做在ACTION_MOVE
中,不过需要注意的是不要在ACTION_DOWN
中返回 true,这里一旦为true,则同一个事件序列ViewGroup
的disPatchTouchEvent
就不会再调用onInterceptTouchEvent
方法了 。那么本次的事件序列后续都由父view接管,子view对这次事件序列也就没有了机会去消耗事件。归纳起来就是一句话: 父控件一旦拦截了事件,那么同一个事件序列的所有事件都将交给它处理。
2.内部拦截法
内部拦截法主要是通过调用父控件的 requestDisallowInterceptTouchEvent
方法,传进去一个boolean
参数值,true为请求父控件不拦截
需要注意的是父控件的onInterceptTouchEvent
方法中的ACTION_DOWN
事件不要拦截,一旦父控件拦截ACTION_DOWN
事件,那么事件无法传递到子元素之中,内部拦截法也就无法起作用了
Good night!
住所,晚上,听着歌,洗漱完毕准备休息