android中view手势滑动冲突的解决方法,主要解决方法有两种,外部和内部拦截。有需要的可以参考下。

Android手势事件的冲突跟点击事件的分发过程息息相关,由三个重要的方法来共同完成,分别是:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。

?
1
public boolean dispatchTouchEvent(MotionEvent ev)

这个方法用来进行事件的分发。如果事件传递到view,那么这个方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。

?
1
publicboolean onInterceptTouchEvent(MotionEvent event)

在上述方法内部调用,用来判断是拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。

?
1
publicboolean onTouchEvent(MotionEvent event)

在dispathcTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接到事件。

例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
publicbooleandispatchTouchEvent(MotionEvent ev){
     booleanconsume = false;
    if(onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
     else{
       consum = child.dispathcTouchEvent(ev);
    }
  returnconsume;
 }

手势冲突的解决方法就是用上面的三个方法;主要分为两种解决方法:·1外部拦截法 2内部拦截法

1.常见的滑动冲突场景

1.1 外部滑动方向和内部滑动的方向不一致

这种情况我们经常遇见,比如使用viewpaper+listview时,在这种效果中,可以通过左右滑动切换页面,而每一个页面往往又是一个listview,本来在这种情况下是有冲突的,但是Viewpaper内部处理了这个滑动冲突,因此采用viewpaper我们无需关注这个问题,如果我们采用的不是Viewpaper而是ScrollView等,那么必须手动处理滑动冲突,否则内外两层只能有一层滑动,那就是滑动冲突。另外内部左右滑动,外部上下滑动也同样属于该类。

1.2 外部滑动方向和内部滑动方向一致

这种情况就比较复杂,当内外两层都在同一个方向可以滑动的时候,显然存在逻辑问题,因为当手指开始滑动的时候,系统无法知道用户到底是想让那一层动,所以当手指滑动的时候就会出现问题,要么只能一层动,要么内外两成动的都很卡顿。

2.给出解决方案

2.1 外部拦截法

针对场景1,我们可以发现外部和内部的滑动方向不一样也就是说只要判断当前dy和dx的大小,如果dy>dx,那么当前就是竖直滑动,否则就是水平滑动。明确了这个我就就可以根据当前的手势开始拦截了。 

从上一节中我们分析了view的事件分发,我们知道点击事件的分发顺序是 通过父布局分发,如果父布局没有拦截,即onInterceptTouchEvent返回false,才会传递给子View。所以我们就可以利用onInterceptTouchEvent()这个方法来进行事件的拦截。来看一下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
publicboolean onInterceptTouchEvent(MotionEvent event) {
   booleanintercepted = false;
   intx = (int) event.getX();
   inty = (int) event.getY();
   switch(event.getAction()) {
   caseMotionEvent.ACTION_DOWN: {
     intercepted = false;
     break;
   }
   caseMotionEvent.ACTION_MOVE: {
     if(父容器拦截的规则){
       intercepted=true;
     }else{
       intercepted=false;
     }
     break;
   }
   caseMotionEvent.ACTION_UP: {
     intercepted = false;
     break;
   }
   default:
     break;
   }
   mLastXIntercept=x;
   mLastYIntercept=y;
   returnintercepted;
 }

上面的代码差多就是外部拦截的通用模板了,在onInterceptTouchEvent方法中,

首先是ACTION_DOWN这个事件,父容器必须返回false,即不拦截事件,因为一旦父容器拦截了ACTION_DOWN这个事件,那么后续的ACTION_MOVE和ACTION_UP事件将直接交给父容器处理,这个时候事件没法继续传递给子元素了;

然后是ACTION_MOVE这个事件,这个事件可以根据需要决定是否拦截,如果父容器需要拦截就返回true,否则返回false;

最后是ACTION_UP这个事件,这里必须返回false,因为这个事件本身也没有太多意义。

下面我们来具体做一下拦截的操作,我们需要在水平滑动的时候父容器拦截事件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
publicboolean onInterceptTouchEvent(MotionEvent event) {
    booleanintercepted = false;
    intx = (int) event.getX();
    inty = (int) event.getY();
    switch(event.getAction()) {
    caseMotionEvent.ACTION_DOWN: {
      intercepted = false;
      break;
    }
    caseMotionEvent.ACTION_MOVE: {
      intdeltaX=x-mLastXIntercept;
      intdeltaY=y=mLastYIntercept;
      if(Math.abs(deltaX)>Math.abs(deltaY)){
        intercepted=true;
      }else{
        intercepted=false;
      }
      break;
    }
    caseMotionEvent.ACTION_UP: {
      intercepted = false;
      break;
    }
    default:
      break;
    }
    mLastXIntercept=x;
    mLastYIntercept=y;
    returnintercepted;
  }

从上面的代码来看,我们只是修改了一下拦截条件而已,所以说外部拦截还是很简单方便的。在滑动的过程中,当水平方向的距离大时就判定水平滑动。

还是一贯我们做实验来证明理论的风格,我们来自定义一个HorizontalScrollView来体现一下用外部拦截法解决冲突的快感。

先上一下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
packagecom.gxl.viewtest;
importandroid.animation.Animator;
importandroid.animation.ObjectAnimator;
importandroid.content.Context;
importandroid.text.LoginFilter;
importandroid.util.AttributeSet;
importandroid.util.Log;
importandroid.view.GestureDetector;
importandroid.view.MotionEvent;
importandroid.view.VelocityTracker;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.Scroller;
/**
 * s
 * Created by GXL on 2016/7/25 0025.
 */
publicclass HorizontalScrollView extendsViewGroup {
  privatefinal String TAG = "HorizontalScrollView";
  privateVelocityTracker mVelocityTracker;
  privateScroller mScroller;
  privateint mChildrenSize;
  privateint mChildWidth;
  privateint mChildIndex;
  //上次滑动的坐标
  privateint mLastX = 0;
  privateint mLastY = 0;
  //上次上次拦截滑动的坐标
  privateint mLastXIntercept = 0;
  privateint mLastYIntercept = 0;
  publicHorizontalScrollView(Context context) {
    super(context);
    init(context);
  }
  publicHorizontalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }
  publicHorizontalScrollView(Context context, AttributeSet attrs, intdefStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }
  publicvoid init(Context context) {
    mVelocityTracker = VelocityTracker.obtain();
    mScroller = newScroller(context);
  }
  publicboolean onInterceptTouchEvent(MotionEvent event) {
    booleanintercepted = false;
    intx = (int) event.getX();
    inty = (int) event.getY();
    switch(event.getAction()) {
      caseMotionEvent.ACTION_DOWN: {
        intercepted = false;
        break;
      }
      caseMotionEvent.ACTION_MOVE: {
        intdeltaX = x - mLastXIntercept;
        intdeltaY = y - mLastYIntercept;
        if(Math.abs(deltaX) > Math.abs(deltaY)) {
          intercepted = true;
        }else{
          intercepted = false;
        }
        break;
      }
      caseMotionEvent.ACTION_UP: {
        intercepted = false;
        break;
      }
      default:
        break;
    }
    mLastX = x;
    mLastY = y;
    mLastXIntercept = x;
    mLastYIntercept = y;
    returnintercepted;
  }
  @Override
  publicboolean onTouchEvent(MotionEvent event) {
    mVelocityTracker.addMovement(event);
    intx = (int) event.getX();
    inty = (int) event.getY();
    switch(event.getAction()) {
      caseMotionEvent.ACTION_DOWN:
        break;
      caseMotionEvent.ACTION_MOVE:
        intdeltaX = x - mLastX;
        if((getScrollX()-deltaX)>=0&&(getScrollX()-deltaX)<=(getMeasuredWidth()-ScreenUtils.getScreenWidth(getContext()))) {
          scrollBy(-deltaX,0);
        }
        break;
      caseMotionEvent.ACTION_UP:
        mVelocityTracker.computeCurrentVelocity(1000);
        floatxVelocityTracker = mVelocityTracker.getXVelocity();
        if(Math.abs(xVelocityTracker) > 50) {
          if(xVelocityTracker > 0) {
            Log.i(TAG,"快速向右划");
          }else{
            Log.i(TAG,"快速向左划");
          }
        }
        mVelocityTracker.clear();
        break;
    }
    mLastX = x;
    mLastY = y;
    returntrue;
  }
  @Override
  protectedvoid onMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    intmeasuredWidth = 0;
    intmeasureHeight = 0;
    finalint childCount = getChildCount();
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    intwidthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
    intwidthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
    intheightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
    intheightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);
    if(childCount == 0) {
      setMeasuredDimension(0,0);
    }elseif (heightSpaceMode == MeasureSpec.AT_MOST && widthSpaceMode == MeasureSpec.AT_MOST) {
      finalView childView = getChildAt(0);
      measuredWidth = childView.getMeasuredWidth() * childCount;
      measureHeight = childView.getMeasuredHeight();
      setMeasuredDimension(measuredWidth, measureHeight);
    }elseif (heightSpaceMode == MeasureSpec.AT_MOST) {
      measureHeight = getChildAt(0).getMeasuredHeight();
      setMeasuredDimension(widthSpaceSize, measureHeight);
    }elseif (widthSpaceMode == MeasureSpec.AT_MOST) {
      finalView childView = getChildAt(0);
      measuredWidth = childView.getMeasuredWidth() * childCount;
      setMeasuredDimension(measuredWidth, heightSpaceSize);
    }
  }
  @Override
  protectedvoid onLayout(booleanchanged, intl, intt, intr, intb) {
    Log.i(TAG,"onLayout: " + getMeasuredWidth());
    intchildleft = 0;
    finalint childCount = getChildCount();
    mChildrenSize = childCount;
    for(inti = 0; i < mChildrenSize; i++) {
      finalView childView = getChildAt(i);
      if(childView.getVisibility() != View.GONE) {
        finalint childWidth = childView.getMeasuredWidth();
        mChildWidth = childWidth;
        childView.layout(childleft,0, childleft + mChildWidth, childView.getMeasuredHeight());
        childleft += childWidth;
      }
    }
  }
  privatevoid smoothScrollTo(intdestX,intdestY)
  {
    intscrollX=getScrollX();
    intdelta=destX-scrollX;
    mScroller.startScroll(scrollX,0,delta,0,1000);
  }
  @Override
  publicvoid computeScroll() {
    if(mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      postInvalidate();
    }
  }
}

再来看一下布局文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<com.gxl.viewtest.HorizontalScrollView
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:background="#00ff00"
   >
   <ListView
     android:id="@+id/listview1"
     android:layout_width="600dp"
     android:layout_height="match_parent"
     android:background="@color/colorPrimary"
     >
   </ListView>
   <ListView
     android:id="@+id/listview2"
     android:layout_width="600dp"
     android:layout_height="match_parent"
     android:background="@color/colorAccent"
     >
   </ListView>
   <ListView
     android:id="@+id/listview3"
     android:layout_width="600dp"
     android:layout_height="match_parent"
     android:background="#ff0000"
     >
   </ListView>
 </com.gxl.viewtest.HorizontalScrollView>

以上就是外部处理滑动冲突的代码,认真看一下,思路还是很清晰的。里面还涉及了一些自定义View的知识,我会在后面的博文中认真分析一下代码,你先看一下onInterceptTouchEvent处理滑动冲突的部分。 
看一下效果图哈。 

2.2 内部拦截法

内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器去处理,这种方法和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,这个方法的大体解释就是:

requestDisallowInterceptTouchEvent是ViewGroup类中的一个公用方法,参数是一个boolean值,官方介绍如下

Called when a child does not want this parent and its ancestors to intercept touch events with ViewGroup.onInterceptTouchEvent(MotionEvent). 
This parent should pass this call onto its parents. This parent must obey this request for the duration of the touch (that is, only clear the flag after this parent has received an up or a cancel.

android系统中,一次点击事件是从父view传递到子view中,每一层的view可以决定是否拦截并处理点击事件或者传递到下一层,如果子view不处理点击事件,则该事件会传递会父view,由父view去决定是否处理该点击事件。在子view可以通过设置此方法去告诉父view不要拦截并处理点击事件,父view应该接受这个请求直到此次点击事件结束。

使用起来外部拦截事件略显复杂一点。下面我也先来看一下它的通用模板(注意下面的代码是定义在子View中的):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
publicboolean onInterceptTouchEvent(MotionEvent event) {
    intx = (int) event.getX();
    inty = (int) event.getY();
    switch(event.getAction()) {
    caseMotionEvent.ACTION_DOWN: {
      parent.requestDisallowInterceptTouchEvent(true);//父布局不要拦截此事件
      break;
    }
    caseMotionEvent.ACTION_MOVE: {
      intdeltaX=x-mLastXIntercept;
      intdeltaY=y=mLastYIntercept;
      if(父容器需要拦截的事件){
        parent.requestDisallowInterceptTouchEvent(false);//父布局需要要拦截此事件
      }
      break;
    }
    caseMotionEvent.ACTION_UP: {
      intercepted = false;
      break;
    }
    default:
      break;
    }
    mLastXIntercept=x;
    mLastYIntercept=y;
    returnsuper.dispathTouchEvent(event);
  }

上述代码是内部拦截法的典型代码,当面对不同的滑动策略时只需要修改里面的条件即可,其他不需要修改做改动而且也不能改动。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

android中view手势滑动冲突的两种解决方法相关推荐

  1. Android中View转换为Bitmap及getDrawingCache=null的解决方法

    Android中View转换为Bitmap及getDrawingCache=null的解决方法 参考文章: (1)Android中View转换为Bitmap及getDrawingCache=null的 ...

  2. php无限极 left right,php无限极分类实现的两种解决方法

    本篇文章介绍了,在php中无限极分类实现的两种解决方法.需要的朋友参考下 method of classify one<?php /* reader: 这是自己写的无限极分类实现方法 里面的编辑 ...

  3. java代码二进制转为十六进制_Java 中二进制转换成十六进制的两种实现方法

    Java 中二进制转换成十六进制的两种实现方法 每个字节转成16进制,方法1 /** * 每个字节转成16进制,方法1 * * @param result */ private static Stri ...

  4. Json返回时间中出现乱码问题的两种解决方法

    Json返回时间中出现乱码问题的两种解决方法 参考文章: (1)Json返回时间中出现乱码问题的两种解决方法 (2)https://www.cnblogs.com/hanyinglong/archiv ...

  5. android平板电脑的虚拟键盘,win10平板电脑不弹出虚拟键盘的两种解决方法

    平板电脑升级到了最新版Windows10系统后,发现在开机的时候没有弹出虚拟键盘,这样就没办法打字了,此问题对我们来说其实是比较少见的,如果真的出现这样的情况怎么办?今天小编就为大家整理了win10平 ...

  6. Visual C++中error spawning cl.exe错误的两种解决方法

    Win10重装VC的朋友们,应该遇到过很对问题,按照网上很多建议装虚拟机和修改文件的文章来操作效果都不是很理想,本人也深受其扰,不过接下来这篇文章救我于水深火热之中. 原文地址为:Visual C++ ...

  7. win10中找不到www服务器,Windows10 cs搜不到局域网的两种解决方法

    CS是一款非常受欢迎的对战单机游戏,能够给我们带来不俗的视觉及听觉体验,而在同个局域网中的用户也可以进行即时对战,非常的有趣.不过,近来一些使用Win10系统的用户反馈在局域网中和室友玩cs的时候搜不 ...

  8. mysql workbench kernelbase.dll_电脑出现kernelbase.dll错误的两种解决方法

    KernelBase.dll是Windows操作系统的重要文件,它为各种应用程序提供服务.如果电脑提示kernelbase.dll错误,这该怎么处理?大家可以用电脑自带的防火墙或者是第三方软件来进行故 ...

  9. but was actually of type 'com.sun.proxy.$Proxy**'的两种解决方法

    错误描述:Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named '****' ...

最新文章

  1. 汽车与智能家居互联时代 语音控制很关键
  2. 深度学习-机器学习(神经网络的应用 上)
  3. 微盟数据库的涅槃之旅
  4. [选拔赛1]花园(矩阵快速幂),JM的月亮神树(最短路),保护出题人(斜率优化)
  5. webapi实现AJAX多文件上传,AJAX调用webapi上传图片或文件
  6. 将字符串或数字倒序输出
  7. HashMap源码详解与对比
  8. 集中火力 专项击破|数据分析可视化广深线下培训火热来袭
  9. hdu 1520 树形dp入门
  10. win10注册mscomm32.ocx失败解决方法
  11. “云边+端”三管齐下,“有蓉”数据库助力四川气象进入天擎时代
  12. JS中如何获取JSON子项的个数或叫length
  13. 缓存的CachePut 冲突 Cacheable
  14. [UNR #3]百鸽笼
  15. 第六十四章 Caché 函数大全 $STACK 函数
  16. Sperax月报 | 2021年3月
  17. 为什么越来越多的人上拼多多,网友:好货多了
  18. java 修改文件MD5值
  19. linux unable to locate tools.jar,Unable to locate tools.jar
  20. android studio 远程调试,Unity3D学习笔记——Android远程真机调试(Unity Remote)

热门文章

  1. 2021年秋招面试真题以及面试技巧分享
  2. 剑指offe-机器人的运动范围
  3. 禾穗HERS | 职场新人第一定律
  4. html表格转excel有科学计数法,table2excel生成excel表格出现科学计数法问题
  5. 苹果手机怎么隐藏照片?给iPhone相册上锁的3种方法
  6. 【转载】2005中文博客排名报告
  7. [2021.8.18]深入理解PackageManagerService
  8. 小动物立体定位架的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  9. Linux apache 默认目录结构
  10. T70次列车(乌鲁木齐 到 北京)的列车时刻表