ScrollView嵌套ListView处理事件冲突
当ListView嵌套在ScrollView中时会有两个问题
列表内容显示不全
滑动事件冲突
第一个问题可以参考ScrollView与ListView嵌套仿微博个人主页中的解决方法。
第二个问题就是下面要讲的废话了。
一、首先要实现的效果是
手指在ListView中滑动时,滑动事件要交给ListView来处理,也就是说手指在ListView的内容区域中可以上下滑动。
ListView中的内容滑动到顶部后如果手指还是继续向下滑(自己模拟一下),此时的滑动事件应该交给ScrollView来处理,也就是说ScrollView可以继续滑动。
ListView中的内容滑动到底部后如果手指还是继续向上滑(自己模拟一下),此时的滑动事件应该交给ScrollView来处理,也就是说ScrollView可以继续滑动。
下面就来一个一个的实现上面所列出的效果
二、手指在ListView中滑动时,滑动事件要交给ListView来处理
1.首先如果不做任何处理,ListView嵌套在ScrollView中时,默认滑动事件是被ScrollView处理掉的,效果是这样的:
我们都知道ViewGroup默认是不拦截事件的,看一下ViewGroup的源码就知道:
public boolean onInterceptTouchEvent(MotionEvent ev) {return false;
}
- 1
- 2
- 3
而ScrollView是继承自FrameLayout的,那为什么ScrollView会自己处理掉滑动事件呢,到ScrollView的源码里一搜,在onInterceptTouchEvent方法中居然有这么触目惊心的一段:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {final int action = ev.getAction();if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {return true;}if (getScrollY() == 0 && !canScrollVertically(1)) {return false;}switch (action & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_MOVE: {final int activePointerId = mActivePointerId;if (activePointerId == INVALID_POINTER) {// If we don't have a valid id, the touch down wasn't on content.break;}final int pointerIndex = ev.findPointerIndex(activePointerId);if (pointerIndex == -1) {Log.e(TAG, "Invalid pointerId=" + activePointerId+ " in onInterceptTouchEvent");break;}final int y = (int) ev.getY(pointerIndex);final int yDiff = Math.abs(y - mLastMotionY);if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {mIsBeingDragged = true;mLastMotionY = y;initVelocityTrackerIfNotExists();mVelocityTracker.addMovement(ev);mNestedYOffset = 0;if (mScrollStrictSpan == null) {mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");}final ViewParent parent = getParent();if (parent != null) {parent.requestDisallowInterceptTouchEvent(true);}}break;}case MotionEvent.ACTION_DOWN: {final int y = (int) ev.getY();if (!inChild((int) ev.getX(), (int) y)) {mIsBeingDragged = false;recycleVelocityTracker();break;}/** Remember location of down touch.* ACTION_DOWN always refers to pointer index 0.*/mLastMotionY = y;mActivePointerId = ev.getPointerId(0);initOrResetVelocityTracker();mVelocityTracker.addMovement(ev);mIsBeingDragged = !mScroller.isFinished();if (mIsBeingDragged && mScrollStrictSpan == null) {mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");}startNestedScroll(SCROLL_AXIS_VERTICAL);break;}case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:/* Release the drag */mIsBeingDragged = false;mActivePointerId = INVALID_POINTER;recycleVelocityTracker();if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {postInvalidateOnAnimation();}stopNestedScroll();break;case MotionEvent.ACTION_POINTER_UP:onSecondaryPointerUp(ev);break;}return mIsBeingDragged;
}
- 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
(1). 首先看到的是如果ScrollView接收到MOVE事件,并且这个mIsBeingDragged为true,它的onInterceptTouchEvent方法直接就返回true了,也就是拦截了滑动事件,交给它自己处理了。
(2). 接着看switch里面case MotionEvent.ACTION_DOWN
,如果按下的时候手指落在了子控件里面mIsBeingDragged置为false。
里面还有这么一句mIsBeingDragged = !mScroller.isFinished();
,mIsBeingDragged就表示当前ScrollView是否在滑动
这mIsBeingDragged有啥卵用呢?联系上面1中所说,假如ScrollView还在滑动的时候,你想去触摸嵌在里面的ListView,没门,ScrollView滑动还没结束呢,继续直接return true。
另外要说的是,不管咋样ScrollView并不会把ListView的点击事件给拦截掉。
(3). 再来看看switch里面case MotionEvent.ACTION_MOVE
,如果y轴方向上的滑动距离大于最小滑动距离,则将mIsBeingDragged设置为true。结合上面第二点所说,啥情况呢?也就是说虽然我手指落在了子View里面,但是如果我要滑动的话,谁也拦不住老纸(Parent)!!
从上面几点来看,ScrollView确实默认会自己处理掉滑动事件。我们想想事件分发的流程,如果父控件拦截了事件,子控件就没办法接收到事件了。那如何才能让ListView来处理滑动事件呢,接着说。
2.想要让ListView来处理滑动事件,首先要重写它的dispatchTouchEvent方法
我们继承ListView实现自己的一个MyListView,重写它的dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {Log.e(TAG, TAG + "dispatchTouchEvent");switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.e(TAG, TAG + "dispatchTouchEvent -> MotionEvent.ACTION_DOWN");downY = ev.getRawY();y = downY;getParent().requestDisallowInterceptTouchEvent(true);break;......}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
在它的 MotionEvent.ACTION_DOWN 事件中调用: getParent().requestDisallowInterceptTouchEvent(true);
这个’requestDisallowInterceptTouchEvent’真绕口,就是说
我[ListView]的父亲啊[getParent()]请求您[request]行行好别让[Disallow]您的onInterceptTouchEvent方法再拦截我的事件了啊[true]
因此如果你在ListView中调用了这个方法之后,父控件(ScrollView)就不会拦截ListView的滑动事件了。ListView的内容也就可以正常滑动了。
三、ListView中的内容滑动到顶部后以及滑动到底部后,事件应该交给ScrollView来处理
在MyListView中实现这两个方法:
public boolean scrollToBottom() {int first = getFirstVisiblePosition();int last = getLastVisiblePosition();int visibleCoutn = getChildCount();int count = getCount();if ((first + visibleCoutn) == count) {return true;}return false;
}public boolean scrollToTop() {int first = getFirstVisiblePosition();int last = getLastVisiblePosition();int visibleCoutn = getChildCount();int count = getCount();if (first == 0) {return true;}return false;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
一个用于判断ListView是否滑动到底部,一个用于判断ListView是否滑动到顶部。
接着继续重写dispatchTouchEvent方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {Log.e(TAG, TAG + "dispatchTouchEvent");switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downY = ev.getRawY();y = downY;getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE:y = ev.getRawY();if (scrollToTop()) {if (y - downY > mTouchSlop) {/*** Point 1 : 如果滑动到顶部,并且手指还想向下滑动,则事件交还给父控件,要求父控件可以拦截事件*/getParent().requestDisallowInterceptTouchEvent(false);return false;} else if (y - downY < -mTouchSlop) {/*** Point 2 : 如果滑动到顶部,并且手指正常向上滑动,则事件由自己处理,要求父控件不许拦截事件*/getParent().requestDisallowInterceptTouchEvent(true);}}if (scrollToBottom()) {if (y - downY < -mTouchSlop) {/*** Point 3 : 如果滑动到底部,并且手指还想向上滑动,则事件交还给父控件,要求父控件可以拦截事件*/getParent().requestDisallowInterceptTouchEvent(false);return false;} else if (y - downY > mTouchSlop) {/*** Point 4 : 如果滑动到底部,并且手指正常向下滑动,则事件由自己处理,要求父控件不许拦截事件*/getParent().requestDisallowInterceptTouchEvent(true);}}break;case MotionEvent.ACTION_UP:break;default:break;}return super.dispatchTouchEvent(ev);
}
- 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
很简单,看注释,实现的效果:
THE END.
ScrollView嵌套ListView处理事件冲突相关推荐
- Android笔记: 解决ScrollView嵌套ListView的冲突方法
范例: 1.MainActivity代码: public class MainActivity extends Activity {// 只要在设置ListView的Adapter后调用此静态方法Ut ...
- scrollview嵌套listview 滑动事件冲突的解决方法
scrollview嵌套listview 滑动事件冲突的解决方法 参考文章: (1)scrollview嵌套listview 滑动事件冲突的解决方法 (2)https://www.cnblogs.co ...
- Android之 如何解决ScrollView 和ListView滑动冲突的问题如何解决ScrollView can host only one direct child
android 采用ScrollView布局时出现异常:ScrollView can host only one direct child. 解决办法:主要是ScrollView内部只能有一个子元素, ...
- 四种方案解决ScrollView嵌套ListView问题
在工作中,曾多次碰到ScrollView嵌套ListView的问题,网上的解决方法有很多种,但是杂而不全.我试过很多种方法,它们各有利弊. 在这里我将会从使用ScrollView嵌套ListView结 ...
- android中ScrollView嵌套ListView或GridView显示位置问题
Android中ScrollView中嵌套ListView或GridView时在开始进入界面时总是显示中间位置,开头的位置显示不出来.这种情况下只需要在ScrollView的父控件中添加以下两行代码即 ...
- ScrollView嵌套ListView后,进入页面不从顶部开始显示的问题解决
ScrollView嵌套ListView后,进入页面不从顶部开始显示的问题解决 首先,正常情况下,如果在ScrollView里嵌套ListView后,会发现ListView只显示1条数据 那么,为了解 ...
- 有关Scrollview嵌套ListView的那些事
有关Scrollview嵌套ListView的那些事 同样作为滑动控件的scrollview与listview,似乎本不应该嵌套使用,可作为研发的我们通常无法左右UI妹子们的奇(fan)思(ren)妙 ...
- android解决ScrollView嵌套ListView不能下拉刷新
为了不误导新人,这篇帖子写的比较早了,这里2016年2月23日21:33:20更新的内容: 千万不要在实际开发中用scrollview嵌套listview\recylerview来处理滑动嵌套, ...
- ApkBus---四种方案解决ScrollView嵌套ListView问题
原文链接:http://www.apkbus.com/forum.php?mod=viewthread&tid=161576 一. 为什么要使用ScrollView嵌套ListView的奇怪的 ...
最新文章
- vim 忽略大小写查找字符串
- Delphi Form Designer (窗体设计器)之二
- 关于web开发字符集问题解决方法
- keepalived 多个应用_Keepalived与LVS部署多个服务
- 娄底二中高考2021成绩查询,2021年娄底高考状元名单公布,娄底高考状元学校资料及最高分...
- js 逆向分析的神器 --- v_jstools
- go map详细使用方法
- netsh命令恢复网络_实例的状态检查和自动恢复
- ctrl z推出查看文本怎么结束_最实用的10个电脑常用快捷键!Ctrl+Z的鬼才反向了解一下!...
- python去重排序_python文本去重并排序
- 20个最漂亮的基于WordPress的企业网站
- 微信点击右上角 在浏览器中打开代码
- 缺失.NET Framework组件的解决方法
- 2021-2027全球与中国氢动力引擎市场现状及未来发展趋势
- 计算机如何连接网络扫描仪,windows系统下怎么共享扫描仪?
- markdown基础语法
- Unity提取模型动画
- PostgreSQL 磁盘空间的保护伞 PG_repack VS 表膨胀
- 基于JAVA电子书阅读系统设计与实现 开题报告
- 《基于ITK和VTK的医学图像处理系统设计与实现》
热门文章
- 基于ECS部署LAMP环境实验记录
- histeq函数实现直方图的均衡化和规定化
- 插值算法C实现(二元全区间)
- 如何解一元一次方程视频_七年级数学教学视频-小邵课堂
- c++排查线程hang住_Kafka学习笔记之kafka高版本Client连接0.9Server引发的血案排查 - 时光飞逝,逝者如斯...
- c语言关键字_C语言初学者必须掌握的关键字!
- [python] 溜了,溜了,七牛云图片资源批量下载 自建图床服务器
- linux下screen工具使用
- apply的调用 http://bbs.51js.com/thread-82017-1-3.html
- java.util.concurrent.locks.Condition 例子程序探讨