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

滑动是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滑动事件冲突

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

(0)

相关推荐

  • Android ListView监听滑动事件的方法(详解)

    ListView的主要有两种滑动事件监听方法,OnTouchListener和OnScrollListener 1.OnTouchListener OnTouchListener方法来自View中的监听事件,可以在监听三个Action事件发生时通过MotionEvent的getX()方法或getY()方法获取到当前触摸的坐标值,来对用户的滑动方向进行判断,并可在不同的Action状态中做出相应的处理 mListView.setOnTouchListener(new View.OnTouchLis

  • Android编程之滑动按钮事件实例详解

    本文实例讲述了Android滑动按钮事件.分享给大家供大家参考,具体如下: 今天纪录一下滑动按钮功能.. 首先效果图: 然后是分别建立三个文件,第一个是main.class,第二个是SlipButton.class,第三个是 onchangeListener.class main.class import Android.app.Activity; import android.os.Bundle; import android.widget.Toast; public class Main e

  • Android中实现监听ScrollView滑动事件

    时候我们需要监听ScroView的滑动情况,比如滑动了多少距离,是否滑到布局的顶部或者底部.可惜的是SDK并没有相应的方法,不过倒是提供了一个 复制代码 代码如下: protected void onScrollChanged(int x, int y, int oldx, int oldy) 方法,显然这个方法是不能被外界调用的,因此就需要把它暴露出去,方便使用.解决方式就是写一个接口, 复制代码 代码如下: package com.example.demo1;    public inter

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

    滑动是Android中不可缺少的一部分,多个滑动必然会产生冲突,比如我们最常见的是ScrollView中嵌套了ListView,一般做法是计算出ListView的总高度,这样就不用去滑动ListView了.又比如一个ViewPager嵌套Fragment,Fragment里面又有ListView,这原本是有滑动冲突的,但是ViewPager内部去帮我们解决了这种冲突.那如果我们要自己解决冲突又该怎么办呢. 下面有两种方式来解决: 外部拦截法 外部拦截法是指在有点击事件时都要经过父容器,那么在父容

  • JS中touchstart事件与click事件冲突的解决方法

    前言 移动互联网是未来的发展趋势,现在国内很多互联网大佬都在争取移动这一块大饼,如微信及支付宝是目前比较成功的例子,当然还有各种APP和web运用. 下面这篇文章主要介绍了关于JS中touchstart事件与click事件冲突解决的相关内容,下面话不多说了,来一起看看详细的介绍吧. 一 · 业务场景的描述 在对已完成的PC站点进行移动端适配时,我们想要站点在移动设备上有更快的响应速度,以带给用户更好的体验,此时,我们应该使用移动设备专用的事件系统,例如,使用 touchstart 事件代替 cl

  • JS中双击和单击事件冲突的解决方法

    在JS中代码中同一功能块中通常同时会用到单击.双击事件,但通常会遇到一个问题,就是在双击的时候即执行了一次双击事件,而且还执行了两次单击事件.此类冲突在ZTree.DHTMLX中经常遇到. 想要解决两个事件冲突,需要对单击事件进行延时,如果在此延时中又监测到单击事件,那么认为此两次单击属于一个双击事件,则只执行双击事件,并第一时间将延时定时器清理,以防止第二次单击生效. 具体代码如下: var clickFlag = null;//是否点击标识(定时器编号) function doOnClick

  • Android滑动事件冲突详解(一)

    首先,我们假设这样一个场景:一个ViewPager里面嵌套一个ViewPager,内部滑动方向和外部滑动方向一样时,该怎么解决这一冲突呢? 针对滑动冲突这里给出两种解决方案:外部拦截法,内部拦截法. 外部拦截法 外部拦截法是指点击事件都先经过父容器的拦截处理,如果父容器需要此拦截事件,就拦截,不需要就不拦截,这种方法比较符合点击事件的分发机制.这种方法代码如下: @Override public boolean onInterceptTouchEvent(MotionEvent ev) { ..

  • Android 中SwipeRefreshLayout与ViewPager滑动事件冲突解决方法

    Android 中SwipeRefreshLayout与ViewPager滑动事件冲突解决方法 问题描述: 开发中发现,SwipeRefreshLayout的下拉刷新,与ViewPager开发的banner的左右滑动事件有一点冲突,导致banner的左右滑动不够顺畅.很容易在banner的左右滑动的过程中,触发SwipeRefreshLayout的下拉刷新,从而导致banner左右滑动的体验很差. 解决方案: 可以在ViewPager的滑动时候设置SwipeRefreshLayout暂时不可用,

  • android中view手势滑动冲突的解决方法

    Android手势事件的冲突跟点击事件的分发过程息息相关,由三个重要的方法来共同完成,分别是:dispatchTouchEvent.onInterceptTouchEvent和onTouchEvent. public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来进行事件的分发.如果事件传递到view,那么这个方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是

  • ViewPager和SlidingPaneLayout的滑动事件冲突解决方法

    问题描述: ViewPager和SlidingPaneLayout的滑动事件冲突. 问题分析: 在手指左右滑动时,SlidingPaneLayout会屏蔽ViewPager的滑动事件. 解决办法: 自定义SlidingPaneLayout类 import android.content.Context; import android.support.v4.view.MotionEventCompat; import android.support.v4.widget.SlidingPaneLay

  • Android ListView与ScrollView冲突的解决方法总结

    Android ListView与ScrollView冲突的解决方法总结 众所周知ListView与ScrollView都具有滚动能力,对于这样的View控件,当ScrollView与ListView相互嵌套会成为一种问题:  问题一:ScrollView与ListView嵌套导致ListView显示不全面  问题二:ScrollView不能正常滑动 解决方式一: ScrollView+LinearLayout+ListView可以换成ScrollView+LinearLayout+Linear

  • Android ViewPager的事件冲突的解决办法

    Android ViewPager的事件冲突的解决办法 当我昨天做viewpager内图片的滑动时,发现图片没有滑动,反而是viewpager滑动了,后来在网上查了资料,解决的事件冲突的问题. @Override public boolean dispatchTouchEvent(MotionEvent ev) { //处理与Viewpager的事件冲突 if (mCurArrayMode==1){ getParent().requestDisallowInterceptTouchEvent(t

  • 外层竖向ScrollView,里层横向ScrollView滑动冲突的解决方法

    实例如下: public class CustomScrollView extends ScrollView { private GestureDetector mGestureDetector; View.OnTouchListener mGestureListener; @SuppressWarnings("deprecation") public CustomScrollView(Context context,AttributeSet attrs) { super(contex

随机推荐