public boolean dispatchTouchEvent(MotionEvent ev) {boolean consume = false;if(onInterceptTouchEvent(ev)){ consume = onTouchEvent(ev);} else { consume = child.dispatchTouchEvent(ev);}return consume; }通过上面的伪代码大家可能对点击事件的传递规则有了更清楚的认识,即:对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true表示它要拦截此事件,接着这个事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false,就表示它不拦截此事件,这是当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件被最终处理。
•图二(一):ACTION_DOWN被View消费了
•图二(二):后续ACTION_MOVE和UP在不被拦截的情况下都会去找VIEW
•图三:后续的被拦截了
•图四:ACTION_DOWN一开始就被拦截
View事件分发源码分析:
•dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent event) {if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true;}return onTouchEvent(event); }如果mOnTouchListener != null,(mViewFlags&ENABLED_MASK)==ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {...return true; }View的onTouchEvent默认都会消耗掉事件(该方法返回true),除非它是不可点击的(clickable和longClickable同时为false)。并且View的longClickable默认为false,clickable属性要分情况,比如Button默认为true,TextView、ImageView默认为false。
public boolean performClick() {sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);if (mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnClickListener.onClick(this); return true;}return false; }这不就是我们熟悉的OnClickListener吗,它原来是在onTouchEvent中被调用的。只要mOnClickListener不是null,就会去调用它的onClick方法。
... 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)) { // Event handled, we have a target now. mMotionTarget = child; return true;} }} }}两种可能会进入if代码段(即事件被分发给子View):
public boolean onInterceptTouchEvent(MotionEvent ev) {return false; }代码很简单,只有一句,即返回false,ViewGroup默认是不拦截的。如果你需要拦截,只要return true就行了,这样该事件就不会往子View传递了,并且如果你在DOWN return true ,则DOWN,MOVE,UP子View都不会捕获到事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获到事件。
@Override public boolean dispatchTouchEvent(MotionEvent event) {getParent().requestDisallowInterceptTouchEvent(true);int action = event.getAction(); switch (action) {case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break;case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break;case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; default: break;}return super.dispatchTouchEvent(event); }getParent().requestDisallowInterceptTouchEvent(true); 这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。
@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {if ((view.getId() == R.id.tousuContentEditText && canVerticalScroll(tousuContentEditText))) { view.getParent().requestDisallowInterceptTouchEvent(true); if (motionEvent.getAction() == MotionEvent.ACTION_UP) {view.getParent().requestDisallowInterceptTouchEvent(false); }}return false; }private boolean canVerticalScroll(EditText editText) {int scrollY = editText.getScrollY();int scrollRange = editText.getLayout().getHeight();int scrollExtent = editText.getHeight() - editText.getCompoundPaddingTop() - editText.getCompoundPaddingBottom();int scrollDifference = scrollRange - scrollExtent;if (scrollDifference == 0) { return false;}return (scrollY > 0) || (scrollY < scrollDifference - 1); }以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。