关于listview重写onintercepttouchevent不响应move和up事件

时间:2022-08-14 19:36:30

首先来讲讲viewgroup的事件分发机制(盗来的图):

关于listview重写onintercepttouchevent不响应move和up事件

首先执行diapatchtouch,在diapatchtouch中调用onintercepttouchevent,在其中判断有没有拦截,如果拦截了就不调用子view的diapatchtouch,调用自身的touch事件,如果不拦截就调用子view 的diapatchtouch,执行view的事件分发机制。

这里也再说说view的分发机制好了:首先必定的dispatchTouch

public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
mOnTouchListener就是我们setontouchlistner时候赋的值,这个值只要我们set了就不会空,第二个判断是是否可点击,最后一个就是我们在setontouchlistener中重写的那个touch方法,在这里如果我们的touch返回false的会就继续调用ontouchevent方法,在其中会调用onclick方法。还有一个要注意的地方,当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action


listview也是viewgroup,所以讲了上面的分发机制有助于我们分析我们的问题:重写listview中的onintercepttouchevent的时候竟然没有响应move和up事件,也就是一个viewgroup的onintercepttouchevent的move和up没有调用,看看上面的图,我们就知道是在dispatchtouch调用的onintercepttouchevent,那就去看下listview的dispatchtouch的方法中到底做了什么手脚。一进去看发现listview中没有重写这个方法,他的父类(AbsListView)中也没有,一直往上跟,只到viewgroup才有这个方法:

public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}

上面就是源码(不同的版本会有差异),可以看到两个地方调用了onintercepttouchevent,而

final View target = mMotionTarget;

这句一般都是target = null,所以就是直接执行super.dispatchtouch,也就相当于执行一个view的dispatchtouch,直接就和touch、touchevent相关了,不再纠缠onintercepttouchevent,onintercepttouchevent也就不被调用了。说到这里我又去继承LinearLayout写了一个自定义的线性布局,然后重写了其中的dispatchtouch、onintercepttouchevent、ontouchEvent方法,然后惊奇的发现,连dispatchEvent都只响应down了,然后我不然ontouchevent返回super.ontouchevent,而是直接返回true(之前说了,返回true可以响应下一个action,false就是不响应),达到了和listview一样的效果。


说到这里就可以做一个总结了:对于所有的viewgroup,down了之后首先进入的就是dispatchtouch,随后判断是否拦截,不拦截分发给子view,拦截执行自己的ontouchevent方法,一般都是不拦截,然后发给子view,这个时候也会有差异,如果子view是可点击的,那么执行子view的dispatch和ontouchevent,否则的话还是会执行viewgroup的ontouchevent,紧接着move事件,很多viewgroup就直接不响应了(直接在down的dis中就返回了false,前提是子view不可点击),up和move差不多,都是根据子view来决定响不响应,更精确的说是子view的dispatchtouch方法的返回值。

差不多事件分发就这些内容,讲的很乱,大家自己自慢慢体会,有空我会整理出更好的文章。


大家可以自己去试验看看:重写dispatchtouch,直接返回true或者false(不调用supr.dspatchtouch)都不会再调用onintercepttouchevent和ontouchevent响应其中的任何事件