程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 深刻解析Andoird運用開辟中View的事宜傳遞

深刻解析Andoird運用開辟中View的事宜傳遞

編輯:關於JAVA

深刻解析Andoird運用開辟中View的事宜傳遞。本站提示廣大學習愛好者:(深刻解析Andoird運用開辟中View的事宜傳遞)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析Andoird運用開辟中View的事宜傳遞正文


上面以點擊某個view以後的事宜傳遞為例子。
起首剖析view裡的dispatchTouchEvent()辦法,它是點擊view履行的第一個辦法。

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

留意:外面包括兩個回調函數 onTouch(),onTouchEvent();假如控件綁定了OnTouchListener,且該控件是enabled,那末就履行onTouch()辦法,假如該辦法前往true,則解釋該觸摸事宜曾經被OnTouchListener監聽器花費失落了,不會再往下分發了;然則假如前往false,則解釋未被花費,持續往下分發到該控件的onTouchEvent()行止理。

然後剖析onTouchEvent()辦法,停止進一步的觸摸事宜處置。

if (((viewFlags & CLICKABLE) == CLICKABLE || 
   (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 
  switch (event.getAction()) { 
   case MotionEvent.ACTION_UP:
    ..... 
      performClick(); //呼應點擊事宜 
    break; 
   case MotionEvent.ACTION_DOWN: 
   ..... break; 
   case MotionEvent.ACTION_CANCEL: 
   ..... break; 
   case MotionEvent.ACTION_MOVE: 
   ..... break; 
  } 
  return true; 
} 
return false;

假如該控件是clickable 、long_clickable的,那末便可以呼應對應事宜,呼應完後前往true持續呼應。好比點擊事宜,先呼應ACTION_DOWN,然後break並前往true,然背工抬起,又從dispatchTouchEvent()分發上去,再呼應ACTION_UP,外面會去performClick()呼應點擊事宜。

呼應點擊事宜

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

外面履行mOnClickListener.onClick(this);即回調綁定監聽器的onClick()函數。

症結點:
onTouch和onTouchEvent的差別,又該若何應用?
答:
當view控件接收到觸摸事宜,假如控件綁定了onTouchListener監聽器,並且該控件是enable,那末就去履行onTouch()辦法,假如前往true,則曾經把觸摸事宜花費失落,不再向下傳遞;假如前往false,那末持續挪用onTouchEvent()事宜。

Android的Touch事宜傳遞到Activity頂層的DecorView(一個FrameLayout)以後,會經由過程ViewGroup一層層往視圖樹的下面傳遞,終究將事宜傳遞給現實吸收的View。上面給出一些主要的辦法。

dispatchTouchEvent
事宜傳遞到一個ViewGroup下面時,會挪用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 :在按下時刻消除一些狀況
    if (actionMasked == MotionEvent.ACTION_DOWN) {
      cancelAndClearTouchTargets(ev);
      //留意這個辦法
      resetTouchState();
    }

    // Attention 2:檢討能否須要攔阻
    final boolean intercepted;
    //假如方才按下 或許 曾經有子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 {
      // 不是一個舉措序列的開端 同時也沒有子View來處置,直接攔阻
      intercepted = true;
    }

     //事宜沒有撤消 同時沒有被以後ViewGroup攔阻,去找能否有子View接盤
    if (!canceled && !intercepted) {
        //假如這是一系列舉措的開端 或許有一個新的Pointer按下 我們須要去找可以或許處置這個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

        //下面說的觸碰點32的限制就是這裡招致
        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);

          //對以後ViewGroup的一切子View停止排序,在下層的放在開端
          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 盤算能否落在點擊區域上
            if (!canViewReceivePointerEvents(child)
                || !isTransformedTouchPointInView(x, y, child, null)) {
              ev.setTargetAccessibilityFocus(false);
              continue;
            }

               //可以或許處置這個Pointer的View能否曾經處置之前的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 : 直接發給子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;
            }

          }
        }

      }
    }

    // 後面曾經找到了吸收事宜的子View,假如為NULL,表現沒有子View來接辦,以後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在前面部門將會觸及,重點留意。

這裡須要指出一點的是,一系列舉措中的分歧Pointer可以分派給分歧的View去呼應。ViewGroup會保護一個PointerId和處置View的列表TouchTarget,一個TouchTarget代表一個可以處置Pointer的子View,固然一個View可以處置多個Pointer,好比兩根手指都在某一個子View區域。TouchTarget外部應用一個int來存儲它能處置的PointerId,一個int32位,這也就是下層為啥最多只能許可同時最多32點觸碰。

看一下Attention 3 處的代碼,我們常常說view的dispatchTouchEvent假如前往false,那末它就不克不及系列舉措前面的舉措,這是為啥呢?由於Attention 3處假如前往false,那末它不會被記載到TouchTarget中,ViewGroup以為你沒有才能處置這個事宜。

這裡可以看到,ViewGroup真正處置事宜是在dispatchTransformedTouchEvent外面,跟出來看看:

dispatchTransformedTouchEvent
private 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;
}

可以看到這裡不論怎樣樣,都邑挪用View的dispatchTouchEvent,這是真正處置這一次點擊事宜的處所。

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設置的onTouch事宜處在一個較高的優先級,假如onTouch履行前往true,那末就不會去走view的onTouchEvent,而我們一些點擊事宜都是在onTouchEvent中處置的,這也是為何onTouch中前往true,view的點擊相干事宜不會被處置。

小小總結一下這個流程
ViewGroup在接收到下級傳上去的事宜時,假如是一系列Touch事宜的開端(ACTION_DOWN),ViewGroup會先看看本身需不須要攔阻這個事宜(onInterceptTouchEvent,ViewGroup的默許完成直接前往false表現不攔阻),接著ViewGroup遍歷本身一切的View。找到以後點擊的誰人View,立時挪用目的View的dispatchTouchEvent。假如目的View的dispatchTouchEvent前往false,那末以為目的View只是在誰人地位罷了,它其實不想接收這個事宜,只想安寧靜靜的做一個View(我靜靜地看著你們裝*)。此時,ViewGroup還會去走一下本身dispatchTouchEvent,Done!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved