国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

View事件機制分析

bergwhite / 3464人閱讀

摘要:注意,事件分發是向下傳遞的,也就是父到子的順序。事件分發機制的本質是要解決,點擊事件由哪個對象發出,經過哪些對象,最終達到哪個對象并最終得到處理。表示以及分發給其中在內部完成被賦值。會自己處理事件。

目錄介紹

01.Android中事件分發順序

1.1 事件分發的對象是誰

1.2 事件分發的本質

1.3 事件在哪些對象間進行傳遞

1.4 事件分發過程涉及方法

1.5 Android中事件分發順序

02.Activity的事件分發機制

2.1 源碼分析

2.2 點擊事件調用順序

2.3 得出結論

03.ViewGroup事件的分發機制

3.1 看一下這個案例

3.2 源碼分析

3.3 得出結論

04.View事件的分發機制

4.1 源碼分析

4.2 得出結論

4.3 驗證結論

05.思考一下

5.1 onTouch()和onTouchEvent()的區別

5.2 Touch事件的后續事件傳遞

好消息

博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!

01.Android中事件分發順序 1.1 事件分發的對象是誰

事件分發的對象是事件。注意,事件分發是向下傳遞的,也就是父到子的順序。

當用戶觸摸屏幕時(View或ViewGroup派生的控件),將產生點擊事件(Touch事件)。

Touch事件相關細節(發生觸摸的位置、時間、歷史記錄、手勢動作等)被封裝成MotionEvent對象

主要發生的Touch事件有如下四種:

MotionEvent.ACTION_DOWN:按下View(所有事件的開始)

MotionEvent.ACTION_MOVE:滑動View

MotionEvent.ACTION_CANCEL:非人為原因結束本次事件

MotionEvent.ACTION_UP:抬起View(與DOWN對應)

事件列:

從手指接觸屏幕至手指離開屏幕,這個過程產生的一系列事件。即當一個MotionEvent 產生后,系統需要把這個事件傳遞給一個具體的 View 去處理

任何事件列都是以DOWN事件開始,UP事件結束,中間有無數的MOVE事件,如下圖:

1.2 事件分發的本質

將點擊事件(MotionEvent)向某個View進行傳遞并最終得到處理

即當一個點擊事件發生后,系統需要將這個事件傳遞給一個具體的View去處理。這個事件傳遞的過程就是分發過程。

Android事件分發機制的本質是要解決,點擊事件由哪個對象發出,經過哪些對象,最終達到哪個對象并最終得到處理。

1.3 事件在哪些對象間進行傳遞

Activity、ViewGroup、View

一個點擊事件產生后,傳遞順序是:Activity(Window) -> ViewGroup -> View

Android的UI界面是由Activity、ViewGroup、View及其派生類組合而成的

View是所有UI組件的基類

一般Button、ImageView、TextView等控件都是繼承父類View

ViewGroup是容納UI組件的容器,即一組View的集合(包含很多子View和子VewGroup),

其本身也是從View派生的,即ViewGroup是View的子類

是Android所有布局的父類或間接父類:項目用到的布局(LinearLayout、RelativeLayout等),都繼承自ViewGroup,即屬于ViewGroup子類。

與普通View的區別:ViewGroup實際上也是一個View,只不過比起View,它多了可以包含子View和定義布局參數的功能。

1.4 事件分發過程涉及方法

事件分發過程由這幾個方法協作完成

dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

1.5 Android中事件分發順序

Android中事件分發順序:

Activity(Window) -> ViewGroup -> View

其中:

super:調用父類方法

true:消費事件,即事件不繼續往下傳遞

false:不消費事件,事件繼續往下傳遞 / 交由給父控件onTouchEvent()處理

充分理解Android分發機制,本質上是要理解:

Activity對點擊事件的分發機制

ViewGroup對點擊事件的分發機制

View對點擊事件的分發機制

02.Activity的事件分發機制 2.1 源碼分析

當一個點擊事件發生時,事件最先傳到Activity的dispatchTouchEvent()進行事件分發

具體是由Activity的Window來完成

我們來看下Activity的dispatchTouchEvent()的源碼

public boolean dispatchTouchEvent(MotionEvent ev) {
    //第一步
    //一般事件列開始都是DOWN,所以這里基本是true
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        //第二步
        onUserInteraction();
    }
    //第三步
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

第一步

一般事件列開始都是DOWN(按下按鈕),所以這里返回true,執行onUserInteraction()

第二步

先來看下onUserInteraction()源碼

public void onUserInteraction() { 
}

從源碼可以看出:

該方法為空方法

從注釋得知:當此activity在棧頂時,觸屏點擊按home,back,menu鍵等都會觸發此方法

所以onUserInteraction()主要用于屏保

第三步

Window類是抽象類,且PhoneWindow是Window類的唯一實現類

superDispatchTouchEvent(ev)是抽象方法

通過PhoneWindow類中看一下superDispatchTouchEvent()的作用

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
    //mDecor是DecorView的實例
    //DecorView是視圖的頂層view,繼承自FrameLayout,是所有界面的父類
}

接下來我們看mDecor.superDispatchTouchEvent(event):

public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
//DecorView繼承自FrameLayout
//那么它的父類就是ViewGroup
而super.dispatchTouchEvent(event)方法,其實就應該是ViewGroup的dispatchTouchEvent()

}

得出結果

執行getWindow().superDispatchTouchEvent(ev)實際上是執行了ViewGroup.dispatchTouchEvent(event)

這樣事件就從 Activity 傳遞到了 ViewGroup

2.2 點擊事件調用順序

三個方法執行順序

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    LogUtils.e("yc----------事件攔截----------");
    return super.onInterceptTouchEvent(e);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    LogUtils.e("yc----------事件分發----------");
    return super.dispatchTouchEvent(ev);
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent e) {
    LogUtils.e("yc----------事件觸摸----------");
    return super.onTouchEvent(e);
}

執行結果如下

yc----------事件分發----------
yc----------事件攔截----------
yc----------事件觸摸----------

下面將用一段偽代碼來闡述上述三個方法的關系和點擊事件傳遞規則

// 點擊事件產生后,會直接調用dispatchTouchEvent分發方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    //代表是否消耗事件
    boolean consume = false;

    if (onInterceptTouchEvent(ev)) {
        //如果onInterceptTouchEvent()返回true則代表當前View攔截了點擊事件
        //則該點擊事件則會交給當前View進行處理
        //即調用onTouchEvent ()方法去處理點擊事件
        consume = onTouchEvent (ev) ;
    } else {
        //如果onInterceptTouchEvent()返回false則代表當前View不攔截點擊事件
        //則該點擊事件則會繼續傳遞給它的子元素
        //子元素的dispatchTouchEvent()就會被調用,重復上述過程
        //直到點擊事件被最終處理為止
        consume = child.dispatchTouchEvent (ev) ;
    }
    return consume;
}

當一個點擊事件發生時,調用順序如下

1.事件最先傳到Activity的dispatchTouchEvent()進行事件分發

2.調用Window類實現類PhoneWindow的superDispatchTouchEvent()

3.調用DecorView的superDispatchTouchEvent()

4.最終調用DecorView父類的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent()

2.3 得出結論

當一個點擊事件發生時,事件最先傳到Activity的dispatchTouchEvent()進行事件分發,最終是調用了ViewGroup的dispatchTouchEvent()方法

這樣事件就從 Activity 傳遞到了 ViewGroup

03.ViewGroup事件的分發機制 3.1 看一下這個案例

布局如下:

結果測試

只點擊Button,發現執行順序:btn1,btn2

再點擊空白處,發現執行順序:btn1,btn2,viewGroup

從上面的測試結果發現:

當點擊Button時,執行Button的onClick(),但ViewGroupLayout注冊的onTouch()不會執行

只有點擊空白區域時才會執行ViewGroupLayout的onTouch();

結論:Button的onClick()將事件消費掉了,因此事件不會再繼續向下傳遞。

3.2 源碼分析

ViewGroup的dispatchTouchEvent()源碼分析,該方法比較復雜,截取幾個重要的邏輯片段進行介紹,來解析整個分發流程。

// 發生ACTION_DOWN事件或者已經發生過ACTION_DOWN,并且將mFirstTouchTarget賦值,才進入此區域,主要功能是攔截器
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
    //disallowIntercept:是否禁用事件攔截的功能(默認是false),即不禁用
    //可以在子View通過調用requestDisallowInterceptTouchEvent方法對這個值進行修改,不讓該View攔截事件
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    //默認情況下會進入該方法
    if (!disallowIntercept) {
        //調用攔截方法
        intercepted = onInterceptTouchEvent(ev); 
        ev.setAction(action);
    } else {
        intercepted = false;
    }
} else {
    // 當沒有觸摸targets,且不是down事件時,開始持續攔截觸摸。
    intercepted = true;
}

這一段的內容主要是為判斷是否攔截。如果當前事件的MotionEvent.ACTION_DOWN,則進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果mFirstTouchTarget != null,即已經發生過MotionEvent.ACTION_DOWN,并且該事件已經有ViewGroup的子View進行處理了,那么也進入判斷,調用ViewGroup onInterceptTouchEvent()方法的值,判斷是否攔截。如果不是以上兩種情況,即已經是MOVE或UP事件了,并且之前的事件沒有對象進行處理,則設置成true,開始攔截接下來的所有事件。這也就解釋了如果子View的onTouchEvent()方法返回false,那么接下來的一些列事件都不會交給他處理。如果VieGroup的onInterceptTouchEvent()第一次執行為true,則mFirstTouchTarget = null,則也會使得接下來不會調用onInterceptTouchEvent(),直接將攔截設置為true。

當ViewGroup不攔截事件的時候,事件會向下分發交由它的子View或ViewGroup進行處理。

  /* 從最底層的父視圖開始遍歷,
   ** 找尋newTouchTarget,即上面的mFirstTouchTarget
   ** 如果已經存在找尋newTouchTarget,說明正在接收觸摸事件,則跳出循環。
    */
for (int i = childrenCount - 1; i >= 0; i--) {
  final int childIndex = customOrder
    ? getChildDrawingOrder(childrenCount, i) : i;
  final View child = (preorderedList == null)
    ? children[childIndex] : preorderedList.get(childIndex);

  // 如果當前視圖無法獲取用戶焦點,則跳過本次循環
  if (childWithAccessibilityFocus != null) {
     if (childWithAccessibilityFocus != child) {
        continue;
     }
     childWithAccessibilityFocus = null;
     i = childrenCount - 1;
  }
  //如果view不可見,或者觸摸的坐標點不在view的范圍內,則跳過本次循環
  if (!canViewReceivePointerEvents(child) 
      || !isTransformedTouchPointInView(x, y, child, null)) {
    ev.setTargetAccessibilityFocus(false);
    continue;
    }

   newTouchTarget = getTouchTarget(child);
   // 已經開始接收觸摸事件,并退出整個循環。
   if (newTouchTarget != null) {
       newTouchTarget.pointerIdBits |= idBitsToAssign;
       break;
    }

    //重置取消或抬起標志位
    //如果觸摸位置在child的區域內,則把事件分發給子View或ViewGroup
    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
        // 獲取TouchDown的時間點
        mLastTouchDownTime = ev.getDownTime();
        // 獲取TouchDown的Index
        if (preorderedList != null) {
           for (int j = 0; j < childrenCount; j++) {
               if (children[childIndex] == mChildren[j]) {
                    mLastTouchDownIndex = j;
                    break;
                }
           }
         } else {
                 mLastTouchDownIndex = childIndex;
                }

      //獲取TouchDown的x,y坐標
      mLastTouchDownX = ev.getX();
      mLastTouchDownY = ev.getY();
      //添加TouchTarget,則mFirstTouchTarget != null。
      newTouchTarget = addTouchTarget(child, idBitsToAssign);
      //表示以及分發給NewTouchTarget
      alreadyDispatchedToNewTouchTarget = true;
      break;
}

dispatchTransformedTouchEvent()方法實際就是調用子元素的dispatchTouchEvent()方法。

其中dispatchTransformedTouchEvent()方法的重要邏輯如下:

if (child == null) {
    handled = super.dispatchTouchEvent(event);
} else {
    handled = child.dispatchTouchEvent(event);
}

由于其中傳遞的child不為空,所以就會調用子元素的dispatchTouchEvent()。如果子元素的dispatchTouchEvent()方法返回true,那么mFirstTouchTarget就會被賦值,同時跳出for循環。

//添加TouchTarget,則mFirstTouchTarget != null。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
 //表示以及分發給NewTouchTarget
 alreadyDispatchedToNewTouchTarget = true;

其中在addTouchTarget(child, idBitsToAssign);內部完成mFirstTouchTarget被賦值。如果mFirstTouchTarget為空,將會讓ViewGroup默認攔截所有操作。如果遍歷所有子View或ViewGroup,都沒有消費事件。ViewGroup會自己處理事件。

3.3 得出結論

Android事件分發是先傳遞到ViewGroup,再由ViewGroup傳遞到View

在ViewGroup中通過onInterceptTouchEvent()對事件傳遞進行攔截

1.onInterceptTouchEvent方法返回true代表攔截事件,即不允許事件繼續向子View傳遞;

2.返回false代表不攔截事件,即允許事件繼續向子View傳遞;(默認返回false)

3.子View中如果將傳遞的事件消費掉,ViewGroup中將無法接收到任何事件。

04.View事件的分發機制 4.1 源碼分析

View中dispatchTouchEvent()的源碼分析

public boolean dispatchTouchEvent(MotionEvent event) {  
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;  
    }  
    return onTouchEvent(event);  
}

從上面可以看出:

只有以下三個條件都為真,dispatchTouchEvent()才返回true;否則執行onTouchEvent(event)方法

第一個條件:mOnTouchListener != null;
第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED;
第三個條件:mOnTouchListener.onTouch(this, event);

下面,我們來看看下這三個判斷條件:

第一個條件:mOnTouchListener!= null

//mOnTouchListener是在View類下setOnTouchListener方法里賦值的
public void setOnTouchListener(OnTouchListener l) { 

//即只要我們給控件注冊了Touch事件,mOnTouchListener就一定被賦值(不為空)
    mOnTouchListener = l;  
}

第二個條件:(mViewFlags & ENABLED_MASK) == ENABLED

該條件是判斷當前點擊的控件是否enable

由于很多View默認是enable的,因此該條件恒定為true

第三個條件:mOnTouchListener.onTouch(this, event)

回調控件注冊Touch事件時的onTouch方法

//手動調用設置
button.setOnTouchListener(new OnTouchListener() {  
    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        return false;  
    }  
});

如果在onTouch方法返回true,就會讓上述三個條件全部成立,從而整個方法直接返回true。

如果在onTouch方法里返回false,就會去執行onTouchEvent(event)方法。

接下來,我們繼續看:onTouchEvent(event)的源碼分析

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;  
        }  
    }  
     //如果該控件是可以點擊的就會進入到下兩行的switch判斷中去;

    if (((viewFlags & CLICKABLE) == CLICKABLE ||  
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
    //如果當前的事件是抬起手指,則會進入到MotionEvent.ACTION_UP這個case當中。

        switch (event.getAction()) {  
            case MotionEvent.ACTION_UP:  
                boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
               // 在經過種種判斷之后,會執行到關注點1的performClick()方法。
               //請往下看關注點1
                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)) {  
            //關注點1
            //請往下看performClick()的源碼分析
                                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;  
        }  
//如果該控件是可以點擊的,就一定會返回true
        return true;  
    }  
//如果該控件是不可以點擊的,就一定會返回false
    return false;  
}

關注點1:

performClick()的源碼分析

public boolean performClick() {  
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  

    if (mOnClickListener != null) {  
        playSoundEffect(SoundEffectConstants.CLICK);  
        mOnClickListener.onClick(this);  
        return true;  
    }  
    return false;  
}

只要mOnClickListener不為null,就會去調用onClick方法;

那么,mOnClickListener又是在哪里賦值的呢?請繼續看:

public void setOnClickListener(OnClickListener l) {  
    if (!isClickable()) {  
        setClickable(true);  
    }  
    mOnClickListener = l;  
}

當我們通過調用setOnClickListener方法來給控件注冊一個點擊事件時,就會給mOnClickListener賦值(不為空),即會回調onClick()。

4.2 得出結論

1.onTouch()的執行高于onClick()

2.每當控件被點擊時:

如果在回調onTouch()里返回false,就會讓dispatchTouchEvent方法返回false,那么就會執行onTouchEvent();如果回調了setOnClickListener()來給控件注冊點擊事件的話,最后會在performClick()方法里回調onClick()。

onTouch()返回false(該事件沒被onTouch()消費掉) = 執行onTouchEvent() = 執行OnClick()

如果在回調onTouch()里返回true,就會讓dispatchTouchEvent方法返回true,那么將不會執行onTouchEvent(),即onClick()也不會執行;

onTouch()返回true(該事件被onTouch()消費掉) = dispatchTouchEvent()返回true(不會再繼續向下傳遞) = 不會執行onTouchEvent() = 不會執行OnClick()

4.3 驗證結論

在回調onTouch()里返回true

TextView textView = findViewById(R.id.tv_13);
//設置OnTouchListener()
textView.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.d("小楊逗比","執行了onTouch(), 動作是:" + event.getAction());
        return true;
    }
});
//設置OnClickListener
textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.d("小楊逗比","執行了onClick()");
    }
});

打印日志如下所示

注意action為0是ACTION_DOWN,為2是ACTION_MOVE,為1是ACTION_UP。

2019-04-04 13:37:58.301 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:0
2019-04-04 13:37:58.315 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:2
2019-04-04 13:37:58.405 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:2
2019-04-04 13:37:58.408 13616-13616/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:1

在回調onTouch()里返回false

打印結果如下所示

2019-04-04 13:41:26.961 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:0
2019-04-04 13:41:26.978 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:2
2019-04-04 13:41:27.072 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:2
2019-04-04 13:41:27.074 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onTouch(), 動作是:1
2019-04-04 13:41:27.076 14006-14006/org.yczbj.ycrefreshview D/小楊逗比: 執行了onClick()

總結:onTouch()返回true就認為該事件被onTouch()消費掉,因而不會再繼續向下傳遞,即不會執行OnClick()。

05.思考一下 5.1 onTouch()和onTouchEvent()的區別

這兩個方法都是在View的dispatchTouchEvent中調用,但onTouch優先于onTouchEvent執行。

如果在onTouch方法中返回true將事件消費掉,onTouchEvent()將不會再執行。

特別注意:請看下面代碼

//&&為短路與,即如果前面條件為false,將不再往下執行
//所以,onTouch能夠得到執行需要兩個前提條件:
//1. mOnTouchListener的值不能為空
//2. 當前點擊的控件必須是enable的。
mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
        mOnTouchListener.onTouch(this, event)

因此如果你有一個控件是非enable的,那么給它注冊onTouch事件將永遠得不到執行。對于這一類控件,如果我們想要監聽它的touch事件,就必須通過在該控件中重寫onTouchEvent方法來實現。

5.2 Touch事件的后續事件(MOVE、UP)層級傳遞

如果給控件注冊了Touch事件,每次點擊都會觸發一系列action事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP等)

當dispatchTouchEvent在進行事件分發的時候,只有前一個事件(如ACTION_DOWN)返回true,才會收到后一個事件(ACTION_MOVE和ACTION_UP)

即如果在執行ACTION_DOWN時返回false,后面一系列的ACTION_MOVE和ACTION_UP事件都不會執行

從上面對事件分發機制分析知:

dispatchTouchEvent()和 onTouchEvent()消費事件、終結事件傳遞(返回true)

而onInterceptTouchEvent 并不能消費事件,它相當于是一個分叉口起到分流導流的作用,對后續的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用

請記住:接收了ACTION_DOWN事件的函數不一定能收到后續事件(ACTION_MOVE、ACTION_UP)

這里給出ACTION_MOVE和ACTION_UP事件的傳遞結論

如果在某個對象(Activity、ViewGroup、View)的dispatchTouchEvent()消費事件(返回true),那么收到ACTION_DOWN的函數也能收到ACTION_MOVE和ACTION_UP

如果在某個對象(Activity、ViewGroup、View)的onTouchEvent()消費事件(返回true),那么ACTION_MOVE和ACTION_UP的事件從上往下傳到這個View后就不再往下傳遞了,而直接傳給自己的onTouchEvent()并結束本次事件傳遞過程。

其他介紹 01.關于博客匯總鏈接

1.技術博客匯總

2.開源項目匯總

3.生活博客匯總

4.喜馬拉雅音頻匯總

5.其他匯總

02.關于我的博客

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

簡書:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜馬拉雅聽書:http://www.ximalaya.com/zhubo...

開源中國:https://my.oschina.net/zbj161...

泡在網上的日子:http://www.jcodecraeer.com/me...

郵箱:yangchong211@163.com

segmentfault頭條:https://segmentfault.com/u/xi...

掘金:https://juejin.im/user/593943...

項目地址:https://github.com/yangchong2...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77658.html

相關文章

  • View事件機制源碼分析

    摘要:當不攔截事件的時候,事件會向下分發交由它的子或進行處理。表示以及分發給其中在內部完成被賦值。會自己處理事件。 目錄介紹 01.Android中事件分發順序 02.Activity的事件分發機制 2.1 源碼分析 2.2 點擊事件調用順序 2.3 得出結論 03.ViewGroup事件的分發機制 3.1 看一下這個案例 3.2 源碼分析 3.3 得出結論 04.Vie...

    antz 評論0 收藏0
  • Android之事件分發機制

    摘要:下事件分發和消費事件前言中與事件相關的方法包括能夠響應的空間包括。事件分析事件分發事件發生時的方法會以隧道方式從根元素依次往下傳遞直到最內層子元素或在中間某一元素中由于某一條件停止傳遞將事件傳遞給最外層的 Android下Touch事件分發和消費事件 前言 Android中與touch事件相關的方法包括:dispatchTouchEvent(MotionEvent ev)、onInte...

    ningwang 評論0 收藏0

發表評論

0條評論

bergwhite

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<