当我们做项目时经常会遇到整体界面需要滑动,此页面中又有ListView的 情况。比如说:某法院系统的提交诉讼材料界面,这种界面要提交很多元素,其中想证人可能有多个,就会用到ListView。但这种ScrollView里 包含ListView的情况下,ListView是无法滚动的。很多人都知道这个现象,但为什么会出现这个问题呢?这就要追溯到我们今天要讲的 android View的事件处理机制。
首先我们先看这个问题为什么会产生呢?我们调查ScrollView的代码会发现,如下
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}
}
这个方法是touch事件的拦截方法,当用户拖动屏幕,就会被这个方法拦截下来,返回true,所以ListView的滚动就失效了。这里又扯出onInterceptTouchEvent方法,这个方法又是什么呢?
View整个体系提供了3个对事件操作:
- dispatchTouchEvent。事件分发,主要是体系的上层向下层,层级形式分发事件的机制。
- onInterceptTouchEvent。事件拦截,主要是拦截事件,并阻止其进一步传递到体系下层的机制。
- onTouchEvent。事件处理,主要是事件接收并做出处理,并向体系上层传递处理结果的机制。
通过这三种操作,对View体系中的事件作出管理。那我们现在已经知道了问题出现的原因:是因为用户的拖动事件被ScrollView的onInterceptTouchEvent方法拦截住了,那事件又是怎么传递过来的呢?
当手机屏幕被用户触摸,就会形成一套事件序列,每一套事件序列都有三种事件动作(down,move,up)组合完成。 这套序列会被传递到系统framework层,再传入Activity,然后Activity会把事件传给Window下的DecorView进行分发, 分发给每个View。而某个View的onTouchEvent处理了这个事件,并返回true,就说明此事件已传递完毕。
说到返回值,这三个方法的返回值含义如下:
- dispatchTouchEvent
true:事件被以该节点为根节点的View树成功处理,此时该事件就算是处理完成了,事件不会再向上返还给View的父节点。
false:以该节点为根节点的View树中,没有一个View成功处理了此事件,所以事件会向上返还给View的父节点。 onInterceptTouchEvent
true:当前ViewGroup希望该事件不再传递给其下层,而是希望自己处理。
false:当前ViewGroup不准备拦截该事件,事件将正常向下分发给其下层。onTouchEvent
true:表示该View成功处理了该事件,该处理结果会向上通知给其上层。
false:表示该View没有成功处理该事件。
到此,了解了这么多再回顾一下开始的问题,原因是事件被ScrollView拦截到了,那解决方法有很多,最直接的一种就是当我们滑动ListView时不让ScrollView拦截到。那要怎么做呢?
通知其上层不要拦截此事件。代码如下:
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
getParent().requestDisallowInterceptTouchEvent(false);
break;
default:
break;
}
return true;
}
以上就是从ListView与ScrollView的冲突调查android view的事件处理机制,希望对大家有所帮助。