一: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)
即可。