ListView 嵌套 ListView,child 是不能滑动的,为什么呢?我们来看看。
一、源码分析
ListView 的滚动在 scrollIfNeeded() 方法中实现。
scrollIfNeed() 方法的调用:
- startScrollIfNeeded() 里返回 true 时
- onTouchMove() 里 mTouchMode == DOWN、TAP、DONE_WAITING 时
- onTouceEvent() 处理 MOVE 事件时
- onInterceptTouchEvent() 处理 MOVE 事件时
- onTouchMove() 里 mTouchMode == SCROLL、OVERSCROLL 时
- onTouceEvent() 处理 MOVE 事件时
mTouchMode 变为 SCROLL 的时机:
- startScrollIfNeeded() 返回 true 时
- onTouchMove() 里 mTouchMode == DOWN、TAP、DONE_WAITING 时
- onTouceEvent() 处理 MOVE 事件时
- onInterceptTouchEvent() 处理 MOVE 事件时
- scrollIfNeeded() 里 mTouchMode == OVERSCROLL 时
- startScrollIfNeeded() 同上
- onTouchMove() mTouchMode == SCROLL、OVERSCROLL 时
- onTouchEvent() 处理 MOVE 事件时
- onTouchDown 里 mTouchMode == FLING 时
- onTouchEvent() 处理 DOWN 事件时
- FlingRunnable (不常用,先忽略)
startScrollIfNeeded() (AbsListView 类)返回 true 的条件:
- overscroll
一般情况下 mScrollY 都是 0,所以 overscroll 都是 false。 - distance > mTouchSlop
mTouchSlop 一般是 8,正常的滑动都会大于,所有一般是 true。 - 滚动轴
要是竖向滚动。
所以竖向的正常滚动,startScrollIfNeeded() 都会返回 true。
二、结论
2.1 不做处理的 ListView 嵌套 ListView
-
正常滑动的 MOVE 事件
父 ListView 的 onInterceptTouchEvent() 中,startScrollIfNeeded() 返回 true,触摸事件由父亲拦截。
随后 mFirstTouchTarget 被置空(dispatch 方法中),之后的 MOVE 事件直接交给父 ListView,不传递给 child。 -
按住不动的 MOVE 事件
父 ListView 的 onInterceptTouchEvent() 中,startScrollIfNeeded() 返回 false,触摸事件传给 child。
随后 mFirstTouchTarget 被赋值(child 的 onTouchEvent() 方法中),之后 dispatch 给 child 的事件不走 onInterceptTouchEvent。
但 child 的 touchMode == TOUCH_MODE_DONE_WAITING,所以也不会调到 scrollIfNeeded。
2.2 重写父 ListView 的 onInterceptTouchEvent(),使其返回 false
-
正常滑动的 MOVE 事件
父 ListView 的 onInterceptTouchEvent() 返回 false,触摸事件传给 child。
随后 mFirstTouchTarget 被赋值(child 的 onTouchEvent() 方法中),之后 dispatch 给 child 的事件不走 onInterceptTouchEvent。
同时 child 会调父 ListView 的 requestDisallowInterceptTouchEvent(),之后 dispatch 给父 ListView 的事件不走 onInterceptTouchEvent。 -
按住不动的 MOVE 事件
父 ListView 的 onInterceptTouchEvent() 返回 false,触摸事件传给 child。
随后 mFirstTouchTarget 被赋值(child 的 onTouchEvent() 方法中),之后 dispatch 给 child 的事件不走 onInterceptTouchEvent。的 onInterceptTouchEvent。