国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

深入解析Andoird應(yīng)用開發(fā)中View的事件傳遞

2019-11-26 14:33:38
字體:
供稿:網(wǎng)友

2016218174438007.jpg (602×339)

下面以點(diǎn)擊某個(gè)view之后的事件傳遞為例子。
首先分析view里的dispatchTouchEvent()方法,它是點(diǎn)擊view執(zhí)行的第一個(gè)方法。

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

注意:里面包含兩個(gè)回調(diào)函數(shù) onTouch(),onTouchEvent();如果控件綁定了OnTouchListener,且該控件是enabled,那么就執(zhí)行onTouch()方法,如果該方法返回true,則說明該觸摸事件已經(jīng)被OnTouchListener監(jiān)聽器消費(fèi)掉了,不會(huì)再往下分發(fā)了;但是如果返回false,則說明未被消費(fèi),繼續(xù)往下分發(fā)到該控件的onTouchEvent()去處理。

然后分析onTouchEvent()方法,進(jìn)行進(jìn)一步的觸摸事件處理。

if (((viewFlags & CLICKABLE) == CLICKABLE ||    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {   switch (event.getAction()) {    case MotionEvent.ACTION_UP:    .....       performClick(); //響應(yīng)點(diǎn)擊事件     break;    case MotionEvent.ACTION_DOWN:    ..... break;    case MotionEvent.ACTION_CANCEL:    ..... break;    case MotionEvent.ACTION_MOVE:    ..... break;   }   return true; } return false;

如果該控件是clickable 、long_clickable的,那么就可以響應(yīng)對(duì)應(yīng)事件,響應(yīng)完后返回true繼續(xù)響應(yīng)。比如點(diǎn)擊事件,先響應(yīng)ACTION_DOWN,然后break并返回true,然后手抬起,又從dispatchTouchEvent()分發(fā)下來,再響應(yīng)ACTION_UP,里面會(huì)去performClick()響應(yīng)點(diǎn)擊事件。

響應(yīng)點(diǎn)擊事件

public boolean performClick() {  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  if (mOnClickListener != null) {     playSoundEffect(SoundEffectConstants.CLICK);     mOnClickListener.onClick(this);     return true;  }  return false;}

里面執(zhí)行mOnClickListener.onClick(this);即回調(diào)綁定監(jiān)聽器的onClick()函數(shù)。

關(guān)鍵點(diǎn):
onTouch和onTouchEvent的區(qū)別,又該如何使用?
答:
當(dāng)view控件接受到觸摸事件,如果控件綁定了onTouchListener監(jiān)聽器,而且該控件是enable,那么就去執(zhí)行onTouch()方法,如果返回true,則已經(jīng)把觸摸事件消費(fèi)掉,不再向下傳遞;如果返回false,那么繼續(xù)調(diào)用onTouchEvent()事件。

Android的Touch事件傳遞到Activity頂層的DecorView(一個(gè)FrameLayout)之后,會(huì)通過ViewGroup一層層往視圖樹的上面?zhèn)鬟f,最終將事件傳遞給實(shí)際接收的View。下面給出一些重要的方法。

dispatchTouchEvent
事件傳遞到一個(gè)ViewGroup上面時(shí),會(huì)調(diào)用dispatchTouchEvent。代碼有刪減

public boolean dispatchTouchEvent(MotionEvent ev) {  boolean handled = false;  if (onFilterTouchEventForSecurity(ev)) {    final int action = ev.getAction();    final int actionMasked = action & MotionEvent.ACTION_MASK;    // Attention 1 :在按下時(shí)候清除一些狀態(tài)    if (actionMasked == MotionEvent.ACTION_DOWN) {      cancelAndClearTouchTargets(ev);      //注意這個(gè)方法      resetTouchState();    }    // Attention 2:檢查是否需要攔截    final boolean intercepted;    //如果剛剛按下 或者 已經(jīng)有子View來處理    if (actionMasked == MotionEvent.ACTION_DOWN        || mFirstTouchTarget != null) {      final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;      if (!disallowIntercept) {        intercepted = onInterceptTouchEvent(ev);        ev.setAction(action); // restore action in case it was changed      } else {        intercepted = false;      }    } else {      // 不是一個(gè)動(dòng)作序列的開始 同時(shí)也沒有子View來處理,直接攔截      intercepted = true;    }     //事件沒有取消 同時(shí)沒有被當(dāng)前ViewGroup攔截,去找是否有子View接盤    if (!canceled && !intercepted) {        //如果這是一系列動(dòng)作的開始 或者有一個(gè)新的Pointer按下 我們需要去找能夠處理這個(gè)Pointer的子View      if (actionMasked == MotionEvent.ACTION_DOWN          || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)          || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {        final int actionIndex = ev.getActionIndex(); // always 0 for down        //上面說的觸碰點(diǎn)32的限制就是這里導(dǎo)致        final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)            : TouchTarget.ALL_POINTER_IDS;        final int childrenCount = mChildrenCount;        if (newTouchTarget == null && childrenCount != 0) {          final float x = ev.getX(actionIndex);          final float y = ev.getY(actionIndex);          //對(duì)當(dāng)前ViewGroup的所有子View進(jìn)行排序,在上層的放在開始          final ArrayList<View> preorderedList = buildOrderedChildList();          final boolean customOrder = preorderedList == null              && isChildrenDrawingOrderEnabled();          final View[] children = mChildren;          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);               // canViewReceivePointerEvents visible的View都可以接受事件               // isTransformedTouchPointInView 計(jì)算是否落在點(diǎn)擊區(qū)域上            if (!canViewReceivePointerEvents(child)                || !isTransformedTouchPointInView(x, y, child, null)) {              ev.setTargetAccessibilityFocus(false);              continue;            }               //能夠處理這個(gè)Pointer的View是否已經(jīng)處理之前的Pointer,那么把            newTouchTarget = getTouchTarget(child);            if (newTouchTarget != null) {              // Child is already receiving touch within its bounds.              // Give it the new pointer in addition to the ones it is handling.              newTouchTarget.pointerIdBits |= idBitsToAssign;              break;            }              }            //Attention 3 : 直接發(fā)給子View            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {              // Child wants to receive touch within its bounds.              mLastTouchDownTime = ev.getDownTime();              if (preorderedList != null) {                // childIndex points into presorted list, find original index                for (int j = 0; j < childrenCount; j++) {                  if (children[childIndex] == mChildren[j]) {                    mLastTouchDownIndex = j;                    break;                  }                }              } else {                mLastTouchDownIndex = childIndex;              }              mLastTouchDownX = ev.getX();              mLastTouchDownY = ev.getY();              newTouchTarget = addTouchTarget(child, idBitsToAssign);              alreadyDispatchedToNewTouchTarget = true;              break;            }          }        }      }    }    // 前面已經(jīng)找到了接收事件的子View,如果為NULL,表示沒有子View來接手,當(dāng)前ViewGroup需要來處理    if (mFirstTouchTarget == null) {      // ViewGroup處理      handled = dispatchTransformedTouchEvent(ev, canceled, null,          TouchTarget.ALL_POINTER_IDS);    } else {        if(alreadyDispatchedToNewTouchTarget) {                   //ignore some code          if (dispatchTransformedTouchEvent(ev, cancelChild,              target.child, target.pointerIdBits)) {            handled = true;         }        }    }  return handled;}

上面代碼中的Attention在后面部分將會(huì)涉及,重點(diǎn)注意。

這里需要指出一點(diǎn)的是,一系列動(dòng)作中的不同Pointer可以分配給不同的View去響應(yīng)。ViewGroup會(huì)維護(hù)一個(gè)PointerId和處理View的列表TouchTarget,一個(gè)TouchTarget代表一個(gè)可以處理Pointer的子View,當(dāng)然一個(gè)View可以處理多個(gè)Pointer,比如兩根手指都在某一個(gè)子View區(qū)域。TouchTarget內(nèi)部使用一個(gè)int來存儲(chǔ)它能處理的PointerId,一個(gè)int32位,這也就是上層為啥最多只能允許同時(shí)最多32點(diǎn)觸碰。

看一下Attention 3 處的代碼,我們經(jīng)常說view的dispatchTouchEvent如果返回false,那么它就不能系列動(dòng)作后面的動(dòng)作,這是為啥呢?因?yàn)锳ttention 3處如果返回false,那么它不會(huì)被記錄到TouchTarget中,ViewGroup認(rèn)為你沒有能力處理這個(gè)事件。

這里可以看到,ViewGroup真正處理事件是在dispatchTransformedTouchEvent里面,跟進(jìn)去看看:

dispatchTransformedTouchEventprivate boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,    View child, int desiredPointerIdBits) {   //沒有子類處理,那么交給viewgroup處理  if (child == null) {    handled = super.dispatchTouchEvent(transformedEvent);  } else {    final float offsetX = mScrollX - child.mLeft;    final float offsetY = mScrollY - child.mTop;    transformedEvent.offsetLocation(offsetX, offsetY);    if (! child.hasIdentityMatrix()) {      transformedEvent.transform(child.getInverseMatrix());    }    handled = child.dispatchTouchEvent(transformedEvent);  }  return handled;}

可以看到這里不管怎么樣,都會(huì)調(diào)用View的dispatchTouchEvent,這是真正處理這一次點(diǎn)擊事件的地方。

dispatchTouchEvent  public boolean dispatchTouchEvent(MotionEvent event) {    if (onFilterTouchEventForSecurity(event)) {    //先走View的onTouch事件,如果onTouch返回True    ListenerInfo li = mListenerInfo;    if (li != null && li.mOnTouchListener != null        && (mViewFlags & ENABLED_MASK) == ENABLED        && li.mOnTouchListener.onTouch(this, event)) {      result = true;    }    if (!result && onTouchEvent(event)) {      result = true;    }  }    return result;  }

我們給View設(shè)置的onTouch事件處在一個(gè)較高的優(yōu)先級(jí),如果onTouch執(zhí)行返回true,那么就不會(huì)去走view的onTouchEvent,而我們一些點(diǎn)擊事件都是在onTouchEvent中處理的,這也是為什么onTouch中返回true,view的點(diǎn)擊相關(guān)事件不會(huì)被處理。

小小總結(jié)一下這個(gè)流程
ViewGroup在接受到上級(jí)傳下來的事件時(shí),如果是一系列Touch事件的開始(ACTION_DOWN),ViewGroup會(huì)先看看自己需不需要攔截這個(gè)事件(onInterceptTouchEvent,ViewGroup的默認(rèn)實(shí)現(xiàn)直接返回false表示不攔截),接著ViewGroup遍歷自己所有的View。找到當(dāng)前點(diǎn)擊的那個(gè)View,馬上調(diào)用目標(biāo)View的dispatchTouchEvent。如果目標(biāo)View的dispatchTouchEvent返回false,那么認(rèn)為目標(biāo)View只是在那個(gè)位置而已,它并不想接受這個(gè)事件,只想安安靜靜的做一個(gè)View(我靜靜地看著你們裝*)。此時(shí),ViewGroup還會(huì)去走一下自己dispatchTouchEvent,Done!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 新兴县| 于都县| 湟源县| 广河县| 辽阳县| 涿鹿县| 三原县| 鹤岗市| 开江县| 黎川县| 海原县| 志丹县| 望谟县| 军事| 雅安市| 平凉市| 霍林郭勒市| 崇阳县| 元谋县| 涿州市| 阜新市| 崇文区| 固阳县| 原平市| 禹城市| 武鸣县| 荔浦县| 萝北县| 逊克县| 册亨县| 麻城市| 隆昌县| 邻水| 安溪县| 龙口市| 晋城| 内乡县| 杭锦后旗| 巴塘县| 云林县| 哈尔滨市|