欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android滑动事件冲突的解决方法

程序员文章站 2023-12-09 18:17:39
滑动是android中不可缺少的一部分,多个滑动必然会产生冲突,比如我们最常见的是scrollview中嵌套了listview,一般做法是计算出listview的总高度,这...

滑动是android中不可缺少的一部分,多个滑动必然会产生冲突,比如我们最常见的是scrollview中嵌套了listview,一般做法是计算出listview的总高度,这样就不用去滑动listview了。又比如一个viewpager嵌套fragment,fragment里面又有listview,这原本是有滑动冲突的,但是viewpager内部去帮我们解决了这种冲突。那如果我们要自己解决冲突又该怎么办呢。

下面有两种方式来解决:

外部拦截法
外部拦截法是指在有点击事件时都要经过父容器,那么在父容器时如果需要拦截就拦截自己处理,不需要则传递给下一层进行处理,下面看个例子:

首先定义一个水平滑动的horizontalscrollviewex,看主要代码

主要的拦截是需要重写onintercepttouchevent

@override 
 public boolean onintercepttouchevent(motionevent ev) { 
 boolean intercepted = false; 
 int x = (int) ev.getx(); 
 int y = (int) ev.gety(); 
 switch (ev.getaction()) { 
 case motionevent.action_down: 
 //down事件不拦截,否则无法传给子元素 
 intercepted = false; 
 if (!mscroller.isfinished()) { 
  mscroller.abortanimation(); 
  intercepted = true; 
 } 
 break; 
 case motionevent.action_move: 
 int deltax = x - mlastxintercept; 
 int deltay = y - mlastyintercept; 
 //水平滑动则拦截 
 if (math.abs(deltax) > math.abs(deltay) + 5) { 
  intercepted = true; 
 } else { 
  intercepted = false; 
 } 
 break; 
 case motionevent.action_up: 
 //不拦截,否则子元素无法收到 
 intercepted = false; 
 break; 
 } 
 //因为当viewgroup中的子view可能消耗了down事件,在ontouchevent无法获取, 
 // 无法对mlastx赋初值,所以在这里赋值一次 
 mlastx = x; 
 mlasty = y; 
 mlastyintercept = y; 
 mlastxintercept = x; 
 return intercepted; 
 } 

在down事件不需要拦截,返回false,否则的话子view无法收到事件,将全部会由父容器处理,这不是希望的;up事件也要返回false,否则最后子view收不到。

看看move事件,当水平滑动距离大于竖直距离时,代表水平滑动,返回true,由父类来进行处理,否则交由子view处理。这里move事件就是主要的拦截条件判断,如果你遇到的不是水平和竖直的条件这么简单,就可以在这里进行改变,比如,scrollview嵌套了listview,条件就变成,当listview滑动到底部或顶部时,返回true,交由父类滑动处理,否则自身listview滑动。

在ontouchevent中主要是做的滑动切换的处理

@override 
 public boolean ontouchevent(motionevent event) { 
 mvelocitytracker.addmovement(event); 
 int x = (int) event.getx(); 
 int y = (int) event.gety(); 
 switch (event.getaction()) { 
 case motionevent.action_down: 
 if (!mscroller.isfinished()) { 
  mscroller.abortanimation(); 
 } 
 break; 
 case motionevent.action_move: 
 int deltax = x - mlastx; 
 int deltay = y - mlasty; 
 if (getscrollx() < 0) { 
  scrollto(0, 0); 
 } 
 scrollby(-deltax, 0); 
 break; 
 case motionevent.action_up: 
 int scrollx = getscrollx(); 
 mvelocitytracker.computecurrentvelocity(1000); 
 float xvelocitytracker = mvelocitytracker.getxvelocity(); 
 if (math.abs(xvelocitytracker) > 50) {//速度大于50则滑动到下一个 
  mchildindex = xvelocitytracker > 0 ? mchildindex - 1 : mchildindex + 1; 
 } else { 
  mchildindex = (scrollx + mchildwith / 2) / mchildwith; 
 } 
 mchildindex = math.max(0, math.min(mchildindex, mchildrensize - 1)); 
 int dx = mchildindex * mchildwith - scrollx; 
 smoothscrollby(dx, 0); 
 mvelocitytracker.clear(); 
 break; 
 } 
 mlasty = y; 
 mlastx = x; 
 return true; 
 } 

在这个嵌套一个普通的listview,这样就可以解决水平和竖直滑动冲突的问题了。

<com.example.lzy.customview.horizontalscrollviewex 
 android:layout_width="match_parent" 
 android:layout_height="200dp"> 
 
 <listview 
 android:id="@+id/listview" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" /> 
 
 <button 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@android:color/holo_blue_bright" 
 android:text="2" /> 
 
 <button 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@android:color/holo_green_dark" 
 android:text="3" /> 
 </com.example.lzy.customview.horizontalscrollviewex> 

其他的部分代码如果需要可以下载源码来看

内部拦截法

内部拦截法是父容器不拦截任何事件,所有事件都传递给子view,如果需要就直接消耗掉,不需要再传给父容器处理

下面重写一个listview,只需要重写一个dispatchtouchevent方法就ok

public class listviewex extends listview { 
 
 private static final string tag = "lzy"; 
 private int mlastx; 
 private int mlasty; 
 
 public listviewex(context context) { 
 super(context); 
 } 
 
 public listviewex(context context, attributeset attrs) { 
 super(context, attrs); 
 } 
 
 public listviewex(context context, attributeset attrs, int defstyleattr) { 
 super(context, attrs, defstyleattr); 
 } 
 
 
 @override 
 public boolean dispatchtouchevent(motionevent ev) { 
 int x = (int) ev.getx(); 
 int y = (int) ev.gety(); 
 
 switch (ev.getaction()) { 
 case motionevent.action_down: 
 //子view的所有父viewgroup都会跳过onintercepttouchevent的回调 
 getparent().requestdisallowintercepttouchevent(true); 
 break; 
 case motionevent.action_move: 
 int deltax = x - mlastx; 
 int deltay = y - mlasty; 
 if (math.abs(deltax) > math.abs(deltay) + 5) {//水平滑动,使得父类可以执行onintercepttouchevent 
  getparent().requestdisallowintercepttouchevent(false); 
 } 
 break; 
 } 
 mlastx = x; 
 mlasty = y; 
 return super.dispatchtouchevent(ev); 
 } 
} 

在down事件调用getparent().requestdisallowintercepttouchevent(true),这句代码的意思是使这个view的父容器都会跳过onintercepttouchevent,在move中判断如果是水平滑动就由父容器去处理,父容器只需要把之前的onintercepttouchevent改为下面那样,其他不变。

@override 
 public boolean onintercepttouchevent(motionevent ev) { 
 int x = (int) ev.getx(); 
 int y = (int) ev.gety(); 
 if (ev.getaction() == motionevent.action_down) { 
 mlastx = x; 
 mlasty = y; 
 if (!mscroller.isfinished()) { 
 mscroller.abortanimation(); 
 return true; 
 } 
 return false; 
 } else { 
 //如果是非down事件,说明子view并没有拦截父类的onintercepttouchevent 
 //说明该事件交由父类处理,所以不需要再传递给子类,返回true 
 return true; 
 } 
 } 

最终实现效果就是下面那样,两个是用两种方式实现的,上面的圆圈是一个简单的自定义view练习

Android滑动事件冲突的解决方法

下载地址:android滑动事件冲突

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