滑动是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 listview的滑动冲突解决方法,包括了Android listview的滑动冲突解决方法的使用技巧和注意事项,需要的朋友参考一下 Android listview的滑动冲突解决方法 在Android开发的过程中,有时候会遇到子控件和父控件都要滑动的情况,尤其是当子控件为listview的时候。就比如在一个ScrollView里有一个listview,这种情况较常见
本文向大家介绍浅谈Android View滑动冲突的解决方法,包括了浅谈Android View滑动冲突的解决方法的使用技巧和注意事项,需要的朋友参考一下 引言 这一篇文章我们就通过介绍滑动冲突的规则和一个实例来更加深入的学习View的事件分发机制。 1、外部滑动方向和内部滑动方向不一致 考虑这样一种场景,开发中我们经常使用ViewPager和Fragment配合使用所组成的页面滑动效果,很多主流
本文向大家介绍解决ViewPager和SlidingPaneLayout的滑动事件冲突问题,包括了解决ViewPager和SlidingPaneLayout的滑动事件冲突问题的使用技巧和注意事项,需要的朋友参考一下 问题描述: ViewPager和SlidingPaneLayout的滑动事件冲突。 问题分析: 在手指左右滑动时,SlidingPaneLayout会屏蔽ViewPager的滑动事件。
本文向大家介绍Android ViewPager的事件冲突的解决办法,包括了Android ViewPager的事件冲突的解决办法的使用技巧和注意事项,需要的朋友参考一下 Android ViewPager的事件冲突的解决办法 当我昨天做viewpager内图片的滑动时,发现图片没有滑动,反而是viewpager滑动了,后来在网上查了资料,解决的事件冲突的问题。 在viewpager内的子view
本文向大家介绍Android解决viewpager嵌套滑动冲突并保留侧滑菜单功能,包括了Android解决viewpager嵌套滑动冲突并保留侧滑菜单功能的使用技巧和注意事项,需要的朋友参考一下 重写子pagerview的dispatchTouchEvent方法,在返回前添加一句getParent().requestDisallowInterceptTouchEvent(true)中断掉事件的传递
我在寻呼机上有三个片段,三个片段都有回收视图。但在浏览回收站视图时,滚动条与之冲突。因此,只有在第三次或第四次轻扫之后,碎片才会被轻扫,或者有时我必须从屏幕开始到结束轻扫,然后碎片才会被重扫。我想要一个平滑的刷液。有人能帮我解决这个问题吗。谢谢