91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Anroid View事件響應機制和ViewGroup的事件響應分發機制

發布時間:2020-07-30 21:06:23 來源:網絡 閱讀:603 作者:屠夫章哥 欄目:移動開發

 

注:低版本的源碼內容比高版本的源碼簡單,分析起來方便,但是高版本源碼更為嚴密。

 

 

View的事件響應機制

涉及2個方法dispatchTouchEventonTouchEvent

 

1.ViewdispatchTouchEvent方法(事件傳遞到ViewView的這個方法就自動執行。)

dispatchTouchEvent返回true,響應事件;返回false,不響應事件。

public boolean dispatchTouchEvent(MotionEvent event) {

       ...

        ListenerInfo li = mListenerInfo;   

 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {

                return true;

            }

 

            if (onTouchEvent(event)) {

                return true;

            }    //等價于return onTouchEvent(event)

 

        }

 


        ...

 

        return false;       //(如果View沒有setOnTouchListener,默認dispatchTouchEvent

   事件就是返回false)

}

 

 

----------------------------------------------------------------------------------------------------------------------

上述源碼中的ListenerInfoView類中的一個靜態成員類,里面封裝了各種事件類型的監聽

XxxListener的變量,包括private OnTouchListener mOnTouchListener;

 

View這個類中有ListenerInfo mListenerInfo;這個類型的成員變量

mListenerInfo是通過下面這個方法來返回的,從方法可以看出返回值肯定不為空。

 ListenerInfo getListenerInfo() {

        if (mListenerInfo != null) {

            return mListenerInfo;

        }

        mListenerInfo = new ListenerInfo();

        return mListenerInfo;

 }

 結論1所以ViewdispatchTouchEvent方法中的if判斷中li(也就是mListenerInfo)!=null

 

 

----------------------------------------------------------------------------------------------------------------------

再看ViewsetOnTouchListener這個方法,只要這個方法被調用了,參數不為空,那么

mListenerInfo.mOnTouchListener就不為空。

 public void setOnTouchListener(OnTouchListener l) {

        getListenerInfo().mOnTouchListener = l;

}

結論2所以ViewdispatchTouchEvent方法中的if判斷中li.mOnTouchListener != null

 

所以

if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event))

這個條件組只是和li.mOnTouchListener.onTouch(this, event)這個回調方法有關

如果返回true的話,ImageViewdispatchTouchEvent方法也返回true

如果返回false的話,會繼續進行下面的一個if判斷,也就是onTouchEvent方法的判斷。

 if (onTouchEvent(event)) {

     return true; 

 }

 上面這3行代碼等價于return onTouchEvent(event),其實2.3.3的源碼就是這么寫的。

 

###############################################################################

所以對于dispatchTouchEvent()方法,如果直接繼承自View的控件

1.沒有調用setOnTouchListener()設置監聽者,那么

  li.mOnTouchListener == nulldispatchTouchEvent()方法默認就會返回false,這時

  onTouchEvent 方法和dispatchTouchEvent()方法就沒有任何的關聯了。

2.調用setOnTouchListener()設置監聽者,那么li.mOnTouchListener = null

  dispatchTouchEvent()方法就會判斷li.mOnTouchListener.onTouch(this, event), 即監聽者

  的onTouch回調方法(表明用戶的意圖)的返回值

  ***onTouch 回調方法返回truedispatchTouchEvent()方法就會返回true

  ***onTouch 回調方法返回falsedispatchTouchEvent()方法就會調用onTouchEvent處理

     事件,即把事件轉交給onTouchEvent方法進行處理

###############################################################################

 

 

2.ViewonTouchEvent方法

public boolean onTouchEvent(MotionEvent event) {

...

if (((viewFlags & CLICKABLE) == CLICKABLE ||

      (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

...

 switch (event.getAction()) {

                case MotionEvent.ACTION_UP:

...

if (!post(mPerformClick)) {

                                    performClick();  /*

底層實現:li.mOnClickListener.onClick(this);

View的點擊事件真正是在UP事件之后執行的。

*/

}

...

return true;

}

return false;

     }

---------------------------------------------------------------------

ViewonTouchEvent的返回值,取決于View是否可點擊。

 這就解釋了為什么當ImageViewButton的監聽事件的OnTouch事件都是返回false時,ImageView只能響應按下事件,而Button能響應所有事件的原因了:因為ImageView默認的clickable屬性為false,而Buttonclickable屬性為true

 

想讓ImageView監聽事件的onTouch返回false也能響應所有的事件,有2種方式

1種:直接將ImageViewclickable屬性設置為true

2種:為ImageView添加點擊事件,通過查看下面的源碼可以看出,點擊事件會將View

    的clickable屬性設置為true

public void setOnClickListener(OnClickListener l) {

        if (!isClickable()) {

            setClickable(true);

        }

        getListenerInfo().mOnClickListener = l;}

 假如Button添加了點擊事件,如果去屏蔽它的點擊事件呢?

在添加點擊事件的代碼后面設置onclickable屬性為false

onTouchListener監聽的onTouch方法里返回true。  

原理:因為View的點擊事件的本質是由onTouchEvent方法中的,performClick

 這個方法所執行的,不讓View執行onTouchEventclickablefalse

 即不會執行點擊事件。

 由此也可以看出onTouch事件和onClick事件不是一回事   

 

 

 

 

 

 

ViewGroup的事件響應和傳遞機制(以2.3.3的源碼來分析)

涉及3個方法dispatchTouchEventonInterceptTouchEventonTouchEvent

 

ViewGroup的事件傳遞,是伴隨著遞歸算法查找坐標落在那一個控件的范圍,由父控件

向子控件,由外到內。

 

Anroid View事件響應機制和ViewGroup的事件響應分發機制 

 @Override

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;  //矩形類 mTempRect = new Rect();

 

        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

 

       

 if (action == MotionEvent.ACTION_DOWN) {

           ...

            // If we're disallowing intercept or if we're allowing and we didn't intercept

//如果onInterceptTouchEvent(ev)沒有攔截事件

//只有自己先不攔截,才有必要去判斷有沒有子View去響應這個事件。

            if (disallowIntercept || !onInterceptTouchEvent(ev)) {

                // reset this event's action (just to protect ourselves)

                ev.setAction(MotionEvent.ACTION_DOWN);

                // We know we want to dispatch the event down, find a child

                // who can handle it, start with the front-most child.

                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

可見或有動畫時

                        child.getHitRect(frame);    //測量子控件的矩形參數

   //判斷事件的坐標有沒有包含在子控件的矩形范圍之內 

                        if (frame.contains(scrolledXInt, scrolledYInt)) {

                           //計算事件在子View中的坐標

                            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.!!!!

  //View如果處理了事件,就把這個View作為事件的目

   標

                                mMotionTarget = child;

                           return true;

                            }

                            // The event didn't get handled, try the next view.

                            // Don't reset the event's location, it's not

                            // necessary here.

                        }

                    }

//View能夠成為mMotionTarget的前提是事件坐標落在它的

矩形范圍之內,并且它響應處理了這個事件。

                }

            }

        }  Action_downif結束

 

 

 

       ...

 

       

 // The event wasn't an ACTION_DOWN, dispatch it to our target if

     we have one.(**沒有子View響應**)

        final View target = mMotionTarget;

        if (target == null) {

            // We don't have a target, this means we're handling the

            // event as a regular view.(**如果沒有子View響應這個事件,這個

事件就會當作一般的View的事件來處理,即ViewGroup執行像View

一樣的去執行事件分發**)

            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 have a target, see if we're allowed to and want to intercept its events**有子View響應**

        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)) {

                // target didn't handle ACTION_CANCEL. not much we can do

                // but they should have.

            }

            // clear the target

            mMotionTarget = null;

            // Don't dispatch this event to our own view, because we already

            // saw it when intercepting; we just want to give the following

            // event to the normal onTouchEvent().

            return true;

        }

 

 

 

       

 

       

 // finally offset the event to the target's coordinate system and

        // dispatch the event.**把事件轉交給子View去處理**

        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);

 

 

}


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武鸣县| 叙永县| 大港区| 左云县| 斗六市| 中江县| 孝义市| 苗栗市| 大余县| 澳门| 宁海县| 平凉市| 静乐县| 徐汇区| 南丰县| 陆良县| 兴仁县| 盈江县| 景洪市| 和田市| 乃东县| 富平县| 咸宁市| 蚌埠市| 乌兰察布市| 新乐市| 延长县| 江门市| 弋阳县| 安泽县| 留坝县| 七台河市| 抚松县| 江华| 公安县| 高清| 韶关市| 湾仔区| 莒南县| 克拉玛依市| 安庆市|