摘要:
上两篇文章分析事件分析流程,这篇文章以及接下来的另一外一篇文章运用这些测试的结果,解决手势冲突的问题。
手势冲突实例一
水平方向的手势和垂直方向的手势冲突解决办法,参考了网上比较多的做法判断移动距离,比较deltaX和deltaY的大小,如果水平方向移动的距离大,判断为水平滑动,否则反之。现在准备了一个例子,ListView里面嵌套ViewPager,ViewPager是左右滑动,ListView是上下滑动,在ListView滑动的时候,调用onInterceptTouchEvent()方法拦截事件,从第一篇《Android事件分发流程分析证明(1)》介绍第一种拦截的方式,即onInterceptTouchEvent返回true
运行Demo的效果:
1、重写MyListView的onInterceptTouchEvent方法,然后返回true,在第一篇分析文章的实验三,可以推测,此时事件无法继续传递,MyListView的onTouchEvent响应事件,打印出deltaX和deltaY的值
- // 分别记录上次滑动的坐标
- private double mLastX = 0;
- private double mLastY = 0;
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- boolean flag = super.onTouchEvent(ev);
- double x = (int) ev.getX();
- double y = (int) ev.getY();
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastX = x;
- mLastY = y;
- Log.d(TAG, "onTouchEvent: ACTION_DOWN " + flag);
- break;
- case MotionEvent.ACTION_MOVE:
- double deltaX = x - mLastX;
- double deltaY = y - mLastY;
- if (Math.abs(deltaY) > Math.abs(deltaX)) {
- }
- Log.d(TAG, "onTouchEvent: ACTION_MOVE " + flag + "\ndeltaX=" + deltaX + "\ndeltaY" + deltaY);
- break;
- case MotionEvent.ACTION_UP:
- Log.d(TAG, "onTouchEvent: ACTION_UP " + flag);
- break;
- }
- return flag;
- }
查看打印log的结果
- D/MyListView: onInterceptTouchEvent: ACTION_DOWN true
这句log说明如果返回true,MyListView将无法接收后面的任何一个事件(ACTION_MOVE或ACTION_UP);target view将会接收到相同的事件,只不过伴随着ACTION_CANCEL事件,接下来的所有事件将会被传递到onTouchEvent()方法,不会再出现在这里。
- D/MyListView: onTouchEvent: ACTION_DOWN true
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=0.0
- deltaY-3.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=1.0
- deltaY-7.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=1.0
- deltaY-9.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=3.0
- deltaY-13.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=3.0
- deltaY-15.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=5.0
- deltaY-20.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=7.0
- deltaY-22.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=9.0
- deltaY-25.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=9.0
- deltaY-27.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=11.0
- deltaY-31.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=11.0
- deltaY-33.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=13.0
- deltaY-36.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=13.0
- deltaY-38.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=15.0
- deltaY-39.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=16.0
- deltaY-45.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=19.0
- deltaY-51.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=18.0
- deltaY-54.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=20.0
- deltaY-56.0
- D/MyListView: onTouchEvent: ACTION_UP true
可以看到onTouchEvent响应事件的按下状态、滑动状态和弹起状态。
但是这个时候的ViewPager处于视图层次结构的下一级,即MyListView的内部,在onInterceptTouchEvent的ACTION_DOWN状态就拦截了事件,在其内部的所有控件将无法滑动,这个时候的ViewPager是无法滑动的。
想要让ViewPager滑动,我们需要修改onIntercetTouchEvent()的逻辑,不再是按下状态就拦截,而是在滑动状态的时候,判断deltaX和deltaY的大小,deltaX大于deltaY,onInterceptTouchEvent返回false,否则反之。
- // 分别记录上次滑动的坐标(onInterceptTouchEvent)
- private int mLastXIntercept = 0;
- private int mLastYIntercept = 0;
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- boolean flag = super.onInterceptTouchEvent(ev);
- double x = (int) ev.getX();
- double y = (int) ev.getY();
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastXIntercept = x;
- mLastYIntercept = y;
- flag=false;
- Log.d(TAG, "onInterceptTouchEvent: ACTION_DOWN " + flag);
- break;
- case MotionEvent.ACTION_MOVE:
- double deltaX = x - mLastXIntercept;
- double deltaY = y - mLastYIntercept;
- if (Math.abs(deltaY) > Math.abs(deltaX)) {
- flag = true;
- }
- Log.d(TAG, "onInterceptTouchEvent: ACTION_MOVE " + flag + "\ndeltaX=" + deltaX + "\ndeltaY" + deltaY);
- break;
- case MotionEvent.ACTION_UP:
- flag=false;
- Log.d(TAG, "onInterceptTouchEvent: ACTION_UP " + flag);
- break;
- }
- return flag;
- }
查看打印log的结果:
- D/MyListView: onInterceptTouchEvent: ACTION_DOWN false
上面这句log返回false,那么接下来的每一个事件都将会首先传递到这里,然后再传递到target的onTouchEvent()方法,所以可以判断此时的ViewPager的onTouchEvent将会响应滑动的手势。
- D/MyListView: onTouchEvent: ACTION_DOWN true
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=0.0
- deltaY=-8.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=0.0
- deltaY=-13.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-23.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-28.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-34.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-35.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-45.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=-2.0
- deltaY=-54.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=1.0
- deltaY=-65.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=3.0
- deltaY=-67.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=4.0
- deltaY=-73.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=9.0
- deltaY=-86.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=12.0
- deltaY=-100.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=14.0
- deltaY=-102.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=17.0
- deltaY=-106.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=17.0
- deltaY=-110.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=18.0
- deltaY=-116.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=24.0
- deltaY=-121.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=26.0
- deltaY=-127.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=28.0
- deltaY=-131.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=30.0
- deltaY=-131.0
- D/MyListView: onTouchEvent: ACTION_UP true
这个时候的ViewPager可以左右滑动,同时ListView也可以上下滑动,ViewPager和ListView的手势冲突的问题基本解决。
为了查看ViewPager的onTouchEvent方法是否响应了滑动事件,我们再来重写ViewPager的onTouchEvent()方法
- D/MyListView: onInterceptTouchEvent: ACTION_DOWN false
- D/MyViewPager: onTouchEvent: ACTION_DOWN true
- D/MyListView: onInterceptTouchEvent: ACTION_MOVE false
- deltaX=-5.0
- deltaY=0.0
在屏幕上滑动ViewPager,看到MyListView的onInterceptTouchEvent返回false,那是因为deltaX>deltaY(比较绝对值的大小),MyListView识别当前手势为左右滑动,通知MyListView不要拦截事件。
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-5.0
- deltaY=0.0
- D/MyListView: onInterceptTouchEvent: ACTION_MOVE false
- deltaX=-17.0
- deltaY=3.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-17.0
- deltaY=3.0
- D/MyListView: onInterceptTouchEvent: ACTION_MOVE false
- deltaX=-34.0
- deltaY=6.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-34.0
- deltaY=6.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-60.0
- deltaY=8.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-83.0
- deltaY=13.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-95.0
- deltaY=15.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-129.0
- deltaY=21.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-157.0
- deltaY=26.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-181.0
- deltaY=31.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-191.0
- deltaY=31.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-221.0
- deltaY=35.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-243.0
- deltaY=37.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-281.0
- deltaY=41.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-310.0
- deltaY=41.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-346.0
- deltaY=37.0
- D/MyViewPager: onTouchEvent: ACTION_MOVE true
- deltaX=-372.0
- deltaY=33.0
- D/MyViewPager: onTouchEvent: ACTION_UP true
可以看到在ViewPager每次滑动后,MyListView都会进行一次识别,当前deltaX和deltaY之间的关系,可以猜想,如果deltaY>deltaX,那么MyListView的onInterceptTouchEvent肯定返回true,然后不再打印出来ViewPager的log信息,最后将可以执行上下滑动的操作。
在ViewPager的位置,上下滑动,查看打印log的结果:
- D/MyListView: onInterceptTouchEvent: ACTION_DOWN false
- D/MyViewPager: onTouchEvent: ACTION_DOWN true
- D/MyListView: onInterceptTouchEvent: ACTION_MOVE true
- deltaX=0.0
- deltaY=-2.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=331.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=330.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=326.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=324.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=321.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=318.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=317.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=441.0
- deltaY=314.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=443.0
- deltaY=309.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=443.0
- deltaY=302.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=443.0
- deltaY=302.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=443.0
- deltaY=300.0
- D/MyListView: onTouchEvent: ACTION_MOVE true
- deltaX=443.0
- deltaY=295.0
- D/MyListView: onTouchEvent: ACTION_UP true
上面的log没有打印MyViewPager的log信息,同时MyListView的onInterceptTouchEvent返回true,证明我们的猜想是正确的,算是完全明白了比较deltaX和deltaY的手势冲突解决办法了,是不是有点小激动呀 <^ _ ^> 。
PS:>在使用的时候,感觉上述这种方式,还是不怎么满意,滑动ViewPager的时候感觉还是有点卡顿,本想要左右滑动的,但稍微水平滑动倾斜了一点,就成了垂直滑动了,造成了误识。
在下面的一篇文章中,我们将通过滑动的倾斜角度,识别事件的拦截,拦截的思路和比较移动距离是一样的,这里通过比较倾斜的角度,只需要将if语句的条件改一下即可
- case MotionEvent.ACTION_MOVE:
- double deltaX =x- mLastX;
- double deltaY =y- mLastY;
- double degrees=calculateDegrees(deltaX,deltaY);
- /**
- *注释掉deltaX和deltaY
- */
- if (isSuitable(degrees)){
- // if (Math.abs(deltaY) > Math.abs(deltaX)) {
- flag=true;
- }
- Log.d(TAG, "dispatchTouchEvent: ACTION_MOVE " + flag);
- break;
- /**
- * 判断是否合适的角度
- * @return
- */
- private boolean isSuitable(double degrees) {
- if (degrees>45&°rees<135|degrees>225&°rees<315)
- return true;
- return false;
- }
- /**
- * 根据反正切计算弧度,再由弧度计算角度
- * @param deltaX
- * @param deltaY
- * @return
- */
- private double calculateDegrees(double deltaX, double deltaY) {
- deltaX=Math.abs(deltaX);
- deltaY=Math.abs(deltaY);
- return Math.toDegrees(Math.atan(deltaY/deltaX));
- }
由于篇幅比较长,将在下一篇文章详细说明如何计算倾斜的角度,通过滑动的角度拦截事件。
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2118.html ,谢谢支持!