一:View事件分发理解
1.View事件执行的顺序为View.onDispatcher-->View.setOnTouchEvent-->View.onTouchEvent,如果setOnTouchEvent中onTouch方法返回true表示该事件被消费,则表示
不执行onTouchEvent,onTouch执行的条件是注册了OnTouch事件,并且View为可点击状态,具体代码如下
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (!onFilterTouchEventForSecurity(event)) {
- return false;
- }
- if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
- mOnTouchListener.onTouch(this, event)) {
- return true;
- }
- return onTouchEvent(event);
- }
2.onTouch方法会优先于OnClickLisener执行,因为OnClick是在onTouchEvent中调用当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发下一个action
- public boolean onTouchEvent(MotionEvent event) {
- final int viewFlags = mViewFlags;
- if ((viewFlags & ENABLED_MASK) == DISABLED) {
- // A disabled view that is clickable still consumes the touch
- // events, it just doesn't respond to them.
- return (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
- }
- if (mTouchDelegate != null) {
- if (mTouchDelegate.onTouchEvent(event)) {
- return true;
- }
- }
- if (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
- if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
- // take focus if we don't have it already and we should in
- // touch mode.
- boolean focusTaken = false;
- if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
- focusTaken = requestFocus();
- }
- if (!mHasPerformedLongPress) {
- // This is a tap, so remove the longpress check
- removeLongPressCallback();
- // Only perform take click actions if we were in the pressed state
- if (!focusTaken) {
- // Use a Runnable and post this rather than calling
- // performClick directly. This lets other visual state
- // of the view update before click actions start.
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {
- performClick();
- }
- }
- }
- if (mUnsetPressedState == null) {
- mUnsetPressedState = new UnsetPressedState();
- }
- if (prepressed) {
- mPrivateFlags |= PRESSED;
- refreshDrawableState();
- postDelayed(mUnsetPressedState,
- ViewConfiguration.getPressedStateDuration());
- } else if (!post(mUnsetPressedState)) {
- // If the post failed, unpress right now
- mUnsetPressedState.run();
- }
- removeTapCallback();
- }
- break;
- case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
- mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- break;
- case MotionEvent.ACTION_CANCEL:
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- removeTapCallback();
- break;
- case MotionEvent.ACTION_MOVE:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- // Be lenient about moving outside of buttons
- int slop = mTouchSlop;
- if ((x < 0 - slop) || (x >= getWidth() + slop) ||
- (y < 0 - slop) || (y >= getHeight() + slop)) {
- // Outside button
- removeTapCallback();
- if ((mPrivateFlags & PRESSED) != 0) {
- // Remove any future long press/tap checks
- removeLongPressCallback();
- // Need to switch from pressed to not pressed
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- }
- }
- break;
- }
- return true;
- }
- return false;
- }
2.setOnLongClickListener和setOnClickLisenter是否只能执行一个?
如果设置setOnLongClickLisener中返回为false,两个都会执行,如果返回true,会屏蔽setOnClickLisener方法
二 ViewGroup事件分发理解
1.ViewGroup继承View覆盖了其dispatchTouchEvent方法,当在Activity中点击一个button,首先会调用该Activity中的dispatchTouchEvent,会拿到setContentVIew中
根节点调用其dispatchTouchEvent方法。
2.在ACTION_DOWN里,首选判断事件是否被拦截,如果没有被拦截,则寻找子View,一旦子View.dispatchTouchEvent 则将这个子View赋值给Target,代表这个子View会消费该事件,如果没有找到这个子View,会调用Super.dispatchTouchEvent 也就是自身来处理该事件,如果自身也不能处理就会调用Activity中onTouchEvent,最终会调用target.dispatchTouchEvent(ev).
3.如果拦截了事件,那么会调用target的ACTION_CANCLE方法,并将target=null,返回true,事件不会向下传递
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } //这个最终传递到setContentView对应的View中 if (getWindow().superDispatchTouchEvent(ev)) { return true; } //如果ContentView没有对时间进行处理,统一由Activity的onTouchEvent()来处理 return onTouchEvent(ev); }
- 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);
- }
-
touch监听器没被调用到?
看View.dispatchTouchEvent()
,ViewGroup.dispatchTouchEvent()
a)如果是View非使能,直接用setEnabled(true)
b)如果是事件被这个View的viewparent拦截了。可以修改这个viewparent的onInterceptTouchTouchEvent()
,或者在这个View中调用getParent().requestDisallowInterceptTouchEvent()
-
双层滑动模块嵌套后发生滑动不了的现象?
看ViewGroup.dispatchTouchEvent()
如果是事件被这个View的viewparent拦截了。可以修改这个viewparent的onInterceptTouchTouchEvent()
,或者在这个View中调用getParent().requestDisallowInterceptTouchEvent()
-
设置了onClickListener后,点击View没有反应?
看View.onTouchEvent()
a)如果是View非使能,直接用setEnabled(true)
b)可能覆盖了onTouchEvent(),需要在覆盖的方法调用super.onTouchEvent()
或者手动调用performClick() -
点击两下View才调用onClickListener的bug?
看View.onTouchEvent()
这个其实是安卓的设计,当某个View调用了setFocusableInTouchMode(true)
后,第一次点击会引起这个View的focus,第二次点击才会调用onClickListener,只需要设置setFocusableInTouchMode(false)
即可。