欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從源碼解析Android中View的容器ViewGroup

 更新時(shí)間:2016年04月05日 15:46:35   作者:xiaanming  
這篇文章主要介紹了Android中View的容器ViewGroup,本文從源碼解析ViewGroup的事件分發(fā)機(jī)制等,非常有深度,需要的朋友可以參考下

 這回我們是深入到ViewGroup內(nèi)部\,了解ViewGroup的工作,同時(shí)會(huì)闡述更多有關(guān)于View的相關(guān)知識(shí)。以便為以后能靈活的使用自定義空間打更近一步的基礎(chǔ)。希望有志同道合的朋友一起來(lái)探討,深入Android內(nèi)部,深入理解Android。

一、ViewGroup是什么?
       一個(gè)ViewGroup是一個(gè)可以包含子View的容器,是布局文件和View容器的基類。在這個(gè)類里定義了ViewGroup.LayoutParams類,這個(gè)類是布局參數(shù)的子類。

       其實(shí)ViewGroup也就是View的容器。通過(guò)ViewGroup.LayoutParams來(lái)指定子View的參數(shù)。

ViewGroup作為一個(gè)容器,為了制定這個(gè)容器應(yīng)有的標(biāo)準(zhǔn)所以為其指定了接口

public abstract class ViewGroup extends View implements ViewParent, ViewManager 
       這兩個(gè)接口這里不研究,如果涉及到的話會(huì)帶一下。ViewGroup有小4000行代碼,下面我們一個(gè)模塊一個(gè)模塊分析。

二、ViewGroup這個(gè)容器
       ViewGroup是一個(gè)容器,其采用一個(gè)數(shù)組來(lái)存儲(chǔ)這些子View:

// Child views of this ViewGroup  
private View[] mChildren; 

       由于是通過(guò)一個(gè)數(shù)組來(lái)存儲(chǔ)View數(shù)據(jù)的,所以對(duì)于ViewGroup來(lái)說(shuō)其必須實(shí)現(xiàn)增、刪、查的算法。下面我們就來(lái)看看其內(nèi)部實(shí)現(xiàn)。

2.1 添加View的算法

  protected boolean addViewInLayout(View child, int index, LayoutParams params) {  
    return addViewInLayout(child, index, params, false);  
  }  
protected boolean addViewInLayout(View child, int index, LayoutParams params,  
      boolean preventRequestLayout) {  
    child.mParent = null;  
    addViewInner(child, index, params, preventRequestLayout);  
    child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;  
    return true;  
  }  
private void addViewInner(View child, int index, LayoutParams params,  
      boolean preventRequestLayout) {  
    ...  
    addInArray(child, index);  
    ...  
  }  
private void addInArray(View child, int index) {  
  ...  
  } 

       上面四個(gè)方法就是添加View的核心算法的封裝,它們是層層調(diào)用的關(guān)系。而我們通常調(diào)用的addView就是最終通過(guò)上面那個(gè)來(lái)最終達(dá)到添加到ViewGroup中的。

   2.1.1 我們先來(lái)分析addViewInner方法:
首先是對(duì)子View是否已經(jīng)包含到一個(gè)父容器中,主要的防止添加一個(gè)已經(jīng)有父容器的View,因?yàn)樘砑右粋€(gè)擁有父容器的View時(shí)會(huì)碰到各種問(wèn)題。比如記錄本身父容器算法的問(wèn)題、本身被多個(gè)父容器包含時(shí)更新的處理等等一系列的問(wèn)題都會(huì)出現(xiàn)。

if (child.getParent() != null) {  
      throw new IllegalStateException("The specified child already has a parent. " +  
          "You must call removeView() on the child's parent first.");  
    } 

然后就是對(duì)子View布局參數(shù)的處理。

調(diào)用addInArray來(lái)添加View

父View為當(dāng)前的ViewGroup

焦點(diǎn)的處理。

當(dāng)前View的AttachInfo信息,這個(gè)信息是用來(lái)在窗口處理中用的。Android的窗口系統(tǒng)就是用過(guò)AttachInfo來(lái)判斷View的所屬窗口的,這個(gè)了解下就行。詳細(xì)信息設(shè)計(jì)到Android框架層的一些東西。

AttachInfo ai = mAttachInfo;  
    if (ai != null) {  
      boolean lastKeepOn = ai.mKeepScreenOn;  
      ai.mKeepScreenOn = false;  
      child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));  
      if (ai.mKeepScreenOn) {  
        needGlobalAttributesUpdate(true);  
      }  
      ai.mKeepScreenOn = lastKeepOn;  
    } 

View樹(shù)改變的監(jiān)聽(tīng)

if (mOnHierarchyChangeListener != null) {  
      mOnHierarchyChangeListener.onChildViewAdded(this, child);  
    } 

子View中的mViewFlags的設(shè)置:

if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {  
      mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;  
    } 

2.1.2 addInArray
       這個(gè)里面的實(shí)現(xiàn)主要是有個(gè)知識(shí)點(diǎn),以前也沒(méi)用過(guò)arraycopy,這里具體實(shí)現(xiàn)就不多加描述了。

System.arraycopy(children, 0, mChildren, 0, index);  
System.arraycopy(children, index, mChildren, index + 1, count - index); 

2.2 移除View
       移除View的幾種方式:

(1)移除指定的View。

(2)移除從指定位置的View

(3)移除從指定位置開(kāi)始的多個(gè)View

(4)移除所有的View

       其中具體涉及到的方法就有好多了,不過(guò)最終對(duì)要?jiǎng)h除的子View中所做的無(wú)非就是下列的事情:

如果擁有焦點(diǎn)則清楚焦點(diǎn)

將要?jiǎng)h除的View從當(dāng)前的window中解除關(guān)系。

設(shè)置View樹(shù)改變的事件監(jiān)聽(tīng),我們可以通過(guò)監(jiān)聽(tīng)OnHierarchyChangeListener事件來(lái)進(jìn)行一些相應(yīng)的處理。

從父容器的子容器數(shù)組中刪除。

       具體的內(nèi)容這里就不一一貼出來(lái)了,大家回頭看看源碼就哦了。

2.3 查詢
       這個(gè)就簡(jiǎn)單了,就是直接從數(shù)組中取出就可以了:

public View getChildAt(int index) {  
  try {  
    return mChildren[index];  
  } catch (IndexOutOfBoundsException ex) {  
    return null;  
  }  
} 

       分析到這兒,其實(shí)我們已經(jīng)相當(dāng)于分析了ViewGroup四分之一的代碼了,呵呵。

三、onFinishInflate
       我們一般使用View的流程是在onCreate中使用setContentView來(lái)設(shè)置要顯示Layout文件或直接創(chuàng)建一個(gè)View,在當(dāng)設(shè)置了ContentView之后系統(tǒng)會(huì)對(duì)這個(gè)View進(jìn)行解析,然后回調(diào)當(dāng)前視圖View中的onFinishInflate方法。只有解析了這個(gè)View我們才能在這個(gè)View容器中獲取到擁有Id的組件,同樣因?yàn)橄到y(tǒng)解析完View之后才會(huì)調(diào)用onFinishInflate方法,所以我們自定義組件時(shí)可以onFinishInflate方法中獲取指定子View的引用。

四、測(cè)量組件
       在ViewGroup中提供了測(cè)量子組件的三個(gè)方法。

1、measureChild(View, int, int),為子組件添加Padding   

  protected void measureChild(View child, int parentWidthMeasureSpec,  
      int parentHeightMeasureSpec) {  
    final LayoutParams lp = child.getLayoutParams();  
   
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
        mPaddingLeft + mPaddingRight, lp.width);  
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
        mPaddingTop + mPaddingBottom, lp.height);  
   
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  } 

2、measureChildren(int, int)根據(jù)指定的高和寬來(lái)測(cè)量所有子View中顯示參數(shù)非GONE的組件。   

  protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  
    final int size = mChildrenCount;  
    final View[] children = mChildren;  
    for (int i = 0; i < size; ++i) {  
      final View child = children[i];  
      if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {  
        measureChild(child, widthMeasureSpec, heightMeasureSpec);  
      }  
    }  
  } 

3、measureChildWithMargins(View, int, int, int, int)測(cè)量指定的子組件,為子組件添加Padding和Margin。   

  protected void measureChildWithMargins(View child,  
      int parentWidthMeasureSpec, int widthUsed,  
      int parentHeightMeasureSpec, int heightUsed) {  
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();  
   
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
        mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin  
            + widthUsed, lp.width);  
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
        mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin  
            + heightUsed, lp.height);  
   
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  } 

       上面三個(gè)方法都是為子組件設(shè)置了布局參數(shù)。最終調(diào)用的方法是子組件的measure方法。在View中我們知道這個(gè)調(diào)用實(shí)際上就是設(shè)置了子組件的布局參數(shù)并且調(diào)用onMeasure方法,最終設(shè)置了View測(cè)量后的高度和寬度。

五、onLayout
       這個(gè)函數(shù)是一個(gè)抽象函數(shù),要求實(shí)現(xiàn)ViewGroup的函數(shù)必須實(shí)現(xiàn)這個(gè)函數(shù),這也就是ViewGroup是一個(gè)抽象函數(shù)的原因。因?yàn)楦鞣N組件實(shí)現(xiàn)的布局方式不一樣,而onLayout是必須被重載的函數(shù)。

@Override 
protected abstract void onLayout(boolean changed,  
    int l, int t, int r, int b); 

 
來(lái)看View中l(wèi)ayout方法:   

public final void layout(int l, int t, int r, int b) {  
  boolean changed = setFrame(l, t, r, b);  
  if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {  
    if (ViewDebug.TRACE_HIERARCHY) {  
      ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);  
    }  
 
    onLayout(changed, l, t, r, b);  
    mPrivateFlags &= ~LAYOUT_REQUIRED;  
  }  
  mPrivateFlags &= ~FORCE_LAYOUT;  
} 

       在這個(gè)方法中調(diào)用了setFrame方法,這個(gè)方法是用來(lái)設(shè)置View中的上下左右邊距用的

  protected boolean setFrame(int left, int top, int right, int bottom) {  
    boolean changed = false;  
    //.......  
    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {  
      changed = true;  
    
      // Remember our drawn bit  
      int drawn = mPrivateFlags & DRAWN;  
    
      // Invalidate our old position  
      invalidate();  
    
    
      int oldWidth = mRight - mLeft;  
      int oldHeight = mBottom - mTop;  
    
      mLeft = left;  
      mTop = top;  
      mRight = right;  
      mBottom = bottom;  
    
      mPrivateFlags |= HAS_BOUNDS;  
    
      int newWidth = right - left;  
      int newHeight = bottom - top;  
    
      if (newWidth != oldWidth || newHeight != oldHeight) {  
        onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);  
      }  
    
      if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {  
        // If we are visible, force the DRAWN bit to on so that  
        // this invalidate will go through (at least to our parent).  
        // This is because someone may have invalidated this view  
        // before this call to setFrame came in, therby clearing  
        // the DRAWN bit.  
        mPrivateFlags |= DRAWN;  
        invalidate();  
      }  
    
      // Reset drawn bit to original value (invalidate turns it off)  
      mPrivateFlags |= drawn;  
    
      mBackgroundSizeChanged = true;  
    }  
    return changed;  
  }  

我們可以看到如果新的高度和寬度改變之后會(huì)調(diào)用重新設(shè)置View的四個(gè)參數(shù):   
(1)protected int mLeft;    
(2)protected int mRight;    
(3)protected int mTop;    
(4)protected int mBottom;   
這四個(gè)參數(shù)指定了View將要布局的位置。而繪制的時(shí)候是通過(guò)這四個(gè)參數(shù)來(lái)繪制,所以我們?cè)赩iew中調(diào)用layout方法可以實(shí)現(xiàn)指定子View中布局。 

六、ViewGroup的繪制。
       ViewGroup的繪制實(shí)際上是調(diào)用的dispatchDraw,繪制時(shí)需要考慮動(dòng)畫(huà)問(wèn)題,而動(dòng)畫(huà)的實(shí)現(xiàn)實(shí)際上就通過(guò)dispatchDraw來(lái)實(shí)現(xiàn)的。

       我們不用理會(huì)太多的細(xì)節(jié),直接看其繪制子組件調(diào)用的是drawChild方法,這個(gè)里面具體的東西就多了,涉及到動(dòng)畫(huà)效果的處理,如果有機(jī)會(huì)的話再寫(xiě),我們只要知道這個(gè)方法的功能就行。

這里有個(gè)demo貼出其中的代碼大家可以測(cè)試下。

public ViewGroup01(Context context)  
{  
  super(context);  
  Button mButton = new Button(context);  
  mButton.setText("測(cè)試");  
  addView(mButton);  
}  
  
@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b)  
{  
  View v = getChildAt(0);  
  if(v != null)  
    {  
    v.layout(120, 120, 250, 250);  
    }  
}  
@Override 
protected void dispatchDraw(Canvas canvas)  
{  
  super.dispatchDraw(canvas);  
  View v = getChildAt(0);  
  if(v != null)  
    {  
    drawChild(canvas, v, getDrawingTime());  
    }  
} 

效果圖片:

201645154246537.png (327×439)

七、ViewGroup的事件分發(fā)機(jī)制
我們用手指去觸摸Android手機(jī)屏幕,就會(huì)產(chǎn)生一個(gè)觸摸事件,但是這個(gè)觸摸事件在底層是怎么分發(fā)的呢?這個(gè)我還真不知道,這里涉及到操作硬件(手機(jī)屏幕)方面的知識(shí),也就是Linux內(nèi)核方面的知識(shí),我也沒(méi)有了解過(guò)這方面的東西,所以我們可能就往上層來(lái)分析分析,我們知道Android中負(fù)責(zé)與用戶交互,與用戶操作緊密相關(guān)的四大組件之一是Activity, 所以我們有理由相信Activity中存在分發(fā)事件的方法,這個(gè)方法就是dispatchTouchEvent(),我們先看其源碼吧

public boolean dispatchTouchEvent(MotionEvent ev) { 
 
    //如果是按下?tīng)顟B(tài)就調(diào)用onUserInteraction()方法,onUserInteraction()方法 
    //是個(gè)空的方法, 我們直接跳過(guò)這里看下面的實(shí)現(xiàn) 
    if (ev.getAction() == MotionEvent.ACTION_DOWN) { 
      onUserInteraction(); 
    } 
     
    if (getWindow().superDispatchTouchEvent(ev)) { 
      return true; 
    } 
     
    //getWindow().superDispatchTouchEvent(ev)返回false,這個(gè)事件就交給Activity 
    //來(lái)處理, Activity的onTouchEvent()方法直接返回了false 
    return onTouchEvent(ev); 
  } 

這個(gè)方法中我們還是比較關(guān)心getWindow()的superDispatchTouchEvent()方法,getWindow()返回當(dāng)前Activity的頂層窗口Window對(duì)象,我們直接看Window API的superDispatchTouchEvent()方法

/** 
   * Used by custom windows, such as Dialog, to pass the touch screen event 
   * further down the view hierarchy. Application developers should 
   * not need to implement or call this. 
   * 
   */ 
  public abstract boolean superDispatchTouchEvent(MotionEvent event); 

這個(gè)是個(gè)抽象方法,所以我們直接找到其子類來(lái)看看superDispatchTouchEvent()方法的具體邏輯實(shí)現(xiàn),Window的唯一子類是PhoneWindow,我們就看看PhoneWindow的superDispatchTouchEvent()方法

public boolean superDispatchTouchEvent(KeyEvent event) { 
    return mDecor.superDispatcTouchEvent(event); 
  } 

里面直接調(diào)用DecorView類的superDispatchTouchEvent()方法,或許很多人不了解DecorView這個(gè)類,DecorView是PhoneWindow的一個(gè)final的內(nèi)部類并且繼承FrameLayout的,也是Window界面的最頂層的View對(duì)象,這是什么意思呢?別著急,我們接著往下看
 
我們先新建一個(gè)項(xiàng)目,取名AndroidTouchEvent,然后直接用模擬器運(yùn)行項(xiàng)目, MainActivity的布局文件為

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  tools:context=".MainActivity" > 
 
  <TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centerHorizontal="true" 
    android:layout_centerVertical="true" 
    android:text="@string/hello_world" /> 
 
</RelativeLayout> 

 
利用hierarchyviewer工具來(lái)查看下MainActivity的View的層次結(jié)構(gòu),如下圖

201645154444322.png (1160×275)

我們看到最頂層就是PhoneWindow$DecorView,接著DecorView下面有一個(gè)LinearLayout, LinearLayout下面有兩個(gè)FrameLayout
上面那個(gè)FrameLayout是用來(lái)顯示標(biāo)題欄的,這個(gè)Demo中是一個(gè)TextView,當(dāng)然我們還可以定制我們的標(biāo)題欄,利用getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.XXX); xxx就是我們自定義標(biāo)題欄的布局XML文件
下面的FrameLayout是用來(lái)裝載ContentView的,也就是我們?cè)贏ctivity中利用setContentView()方法設(shè)置的View,現(xiàn)在我們知道了,原來(lái)我們利用setContentView()設(shè)置Activity的View的外面還嵌套了這么多的東西
我們來(lái)理清下思路,Activity的最頂層窗體是PhoneWindow,而PhoneWindow的最頂層View是DecorView,接下來(lái)我們就看DecorView類的superDispatchTouchEvent()方法

public boolean superDispatchTouchEvent(MotionEvent event) { 
      return super.dispatchTouchEvent(event); 
    } 

在里面調(diào)用了父類FrameLayout的dispatchTouchEvent()方法,而FrameLayout中并沒(méi)有dispatchTouchEvent()方法,所以我們直接看ViewGroup的dispatchTouchEvent()方法

/** 
  * {@inheritDoc} 
  */ 
  @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; 
 
    //這個(gè)值默認(rèn)是false, 然后我們可以通過(guò)requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法 
    //來(lái)改變disallowIntercept的值 
    boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 
 
    //這里是ACTION_DOWN的處理邏輯 
    if (action == MotionEvent.ACTION_DOWN) { 
    //清除mMotionTarget, 每次ACTION_DOWN都很設(shè)置mMotionTarget為null 
      if (mMotionTarget != null) { 
        mMotionTarget = null; 
      } 
 
      //disallowIntercept默認(rèn)是false, 就看ViewGroup的onInterceptTouchEvent()方法 
      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; 
        //遍歷其子View 
        for (int i = count - 1; i >= 0; i--) { 
          final View child = children[i]; 
           
          //如果該子View是VISIBLE或者該子View正在執(zhí)行動(dòng)畫(huà), 表示該View才 
          //可以接受到Touch事件 
          if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 
              || child.getAnimation() != null) { 
          //獲取子View的位置范圍 
            child.getHitRect(frame); 
             
            //如Touch到屏幕上的點(diǎn)在該子View上面 
            if (frame.contains(scrolledXInt, scrolledYInt)) { 
              // offset the event to the view's coordinate system 
              final float xc = scrolledXFloat - child.mLeft; 
              final float yc = scrolledYFloat - child.mTop; 
              ev.setLocation(xc, yc); 
              child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 
               
              //調(diào)用該子View的dispatchTouchEvent()方法 
              if (child.dispatchTouchEvent(ev)) { 
                // 如果child.dispatchTouchEvent(ev)返回true表示 
              //該事件被消費(fèi)了,設(shè)置mMotionTarget為該子View 
                mMotionTarget = child; 
                //直接返回true 
                return true; 
              } 
              // The event didn't get handled, try the next view. 
              // Don't reset the event's location, it's not 
              // necessary here. 
            } 
          } 
        } 
      } 
    } 
 
    //判斷是否為ACTION_UP或者ACTION_CANCEL 
    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || 
        (action == MotionEvent.ACTION_CANCEL); 
 
    if (isUpOrCancel) { 
      //如果是ACTION_UP或者ACTION_CANCEL, 將disallowIntercept設(shè)置為默認(rèn)的false 
    //假如我們調(diào)用了requestDisallowInterceptTouchEvent()方法來(lái)設(shè)置disallowIntercept為true 
    //當(dāng)我們抬起手指或者取消Touch事件的時(shí)候要將disallowIntercept重置為false 
    //所以說(shuō)上面的disallowIntercept默認(rèn)在我們每次ACTION_DOWN的時(shí)候都是false 
      mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 
    } 
 
    // The event wasn't an ACTION_DOWN, dispatch it to our target if 
    // we have one. 
    final View target = mMotionTarget; 
    //mMotionTarget為null意味著沒(méi)有找到消費(fèi)Touch事件的View, 所以我們需要調(diào)用ViewGroup父類的 
    //dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法 
    if (target == null) { 
      // We don't have a target, this means we're handling the 
      // event as a regular 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); 
    } 
 
    //這個(gè)if里面的代碼ACTION_DOWN不會(huì)執(zhí)行,只有ACTION_MOVE 
    //ACTION_UP才會(huì)走到這里, 假如在ACTION_MOVE或者ACTION_UP攔截的 
    //Touch事件, 將ACTION_CANCEL派發(fā)給target,然后直接返回true 
    //表示消費(fèi)了此Touch事件 
    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)) { 
      } 
      // 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; 
    } 
 
    if (isUpOrCancel) { 
      mMotionTarget = null; 
    } 
 
    // finally offset the event to the target's coordinate system and 
    // dispatch the event. 
    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; 
    } 
 
    //如果沒(méi)有攔截ACTION_MOVE, ACTION_DOWN的話,直接將Touch事件派發(fā)給target 
    return target.dispatchTouchEvent(ev); 
  } 

這個(gè)方法相對(duì)來(lái)說(shuō)還是蠻長(zhǎng),不過(guò)所有的邏輯都寫(xiě)在一起,看起來(lái)比較方便,接下來(lái)我們就具體來(lái)分析一下

我們點(diǎn)擊屏幕上面的TextView來(lái)看看Touch是如何分發(fā)的,先看看ACTION_DOWN
在DecorView這一層會(huì)直接調(diào)用ViewGroup的dispatchTouchEvent(), 先看18行,每次ACTION_DOWN都會(huì)將mMotionTarget設(shè)置為null, mMotionTarget是什么?我們先不管,繼續(xù)看代碼,走到25行,  disallowIntercept默認(rèn)為false,我們?cè)倏碫iewGroup的onInterceptTouchEvent()方法

public boolean onInterceptTouchEvent(MotionEvent ev) { 
   return false; 
 } 

直接返回false, 繼續(xù)往下看,循環(huán)遍歷DecorView里面的Child,從上面的MainActivity的層次結(jié)構(gòu)圖我們可以看出,DecorView里面只有一個(gè)Child那就是LinearLayout, 第43行判斷Touch的位置在不在LinnearLayout上面,這是毫無(wú)疑問(wèn)的,所以直接跳到51行, 調(diào)用LinearLayout的dispatchTouchEvent()方法,LinearLayout也沒(méi)有dispatchTouchEvent()這個(gè)方法,所以也是調(diào)用ViewGroup的dispatchTouchEvent()方法,所以這個(gè)方法卡在51行沒(méi)有繼續(xù)下去,而是去先執(zhí)行LinearLayout的dispatchTouchEvent()
LinearLayout調(diào)用dispatchTouchEvent()的邏輯跟DecorView是一樣的,所以也是遍歷LinearLayout的兩個(gè)FrameLayout,判斷Touch的是哪個(gè)FrameLayout,很明顯是下面那個(gè),調(diào)用下面那個(gè)FrameLayout的dispatchTouchEvent(),  所以LinearLayout的dispatchTouchEvent()卡在51也沒(méi)繼續(xù)下去
繼續(xù)調(diào)用FrameLayout的dispatchTouchEvent()方法,和上面一樣的邏輯,下面的FrameLayout也只有一個(gè)Child,就是RelativeLayout,F(xiàn)rameLayout的dispatchTouchEvent()繼續(xù)卡在51行,先執(zhí)行RelativeLayout的dispatchTouchEvent()方法
執(zhí)行RelativeLayout的dispatchTouchEvent()方法邏輯還是一樣的,循環(huán)遍歷 RelativeLayout里面的孩子,里面只有一個(gè)TextView, 所以這里就調(diào)用TextView的dispatchTouchEvent(), TextView并沒(méi)有dispatchTouchEvent()這個(gè)方法,于是找TextView的父類View,在看View的dispatchTouchEvent()的方法之前,我們先理清下上面這些ViewGroup執(zhí)行dispatchTouchEvent()的思路,我畫(huà)了一張圖幫大家理清下(這里沒(méi)有畫(huà)出onInterceptTouchEvent()方法)

201645154529404.png (226×489)

相關(guān)文章

  • Android對(duì)話框AlertDialog詳解

    Android對(duì)話框AlertDialog詳解

    本文詳細(xì)講解了Android對(duì)話框AlertDialog的實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • Android 圖文詳解Binder進(jìn)程通信底層原理

    Android 圖文詳解Binder進(jìn)程通信底層原理

    Android系統(tǒng)中,多進(jìn)程間的通信都是依賴于底層Binder IPC機(jī)制,Binder機(jī)制是一種RPC方案。例如:當(dāng)進(jìn)程A中的Activity與進(jìn)程B中的Service通信時(shí),就使用了binder機(jī)制
    2021-10-10
  • Android教程之開(kāi)機(jī)流程全面解析

    Android教程之開(kāi)機(jī)流程全面解析

    這篇文章主要介紹了Android開(kāi)機(jī)流程,非常全面的分析了Android開(kāi)機(jī)常見(jiàn)形式、函數(shù)調(diào)用與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2016-06-06
  • Android 圖片的三級(jí)緩存機(jī)制實(shí)例分析

    Android 圖片的三級(jí)緩存機(jī)制實(shí)例分析

    這篇文章主要介紹了Android 圖片的三級(jí)緩存機(jī)制實(shí)例分析的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • android開(kāi)發(fā)教程之使用looper處理消息隊(duì)列

    android開(kāi)發(fā)教程之使用looper處理消息隊(duì)列

    這篇文章主要介紹了通過(guò)HandlerThread對(duì)象來(lái)實(shí)現(xiàn)使用looper處理消息隊(duì)列的功能,大家參考使用吧
    2014-01-01
  • Android使用AudioRecord判斷是否有音頻輸入

    Android使用AudioRecord判斷是否有音頻輸入

    這篇文章主要介紹了Android使用AudioRecord判斷是否有音頻輸入的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧
    2017-01-01
  • flutter showModalBottomSheet常用屬性及說(shuō)明

    flutter showModalBottomSheet常用屬性及說(shuō)明

    這篇文章主要介紹了flutter showModalBottomSheet常用屬性及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Android開(kāi)發(fā)之開(kāi)關(guān)按鈕用法示例

    Android開(kāi)發(fā)之開(kāi)關(guān)按鈕用法示例

    這篇文章主要介紹了Android開(kāi)發(fā)之開(kāi)關(guān)按鈕用法,結(jié)合實(shí)例形式分析了Android開(kāi)關(guān)按鈕的具體實(shí)現(xiàn)、布局與功能設(shè)置相關(guān)操作技巧,需要的朋友可以參考下
    2019-04-04
  • Android實(shí)現(xiàn)簡(jiǎn)單手電筒功能

    Android實(shí)現(xiàn)簡(jiǎn)單手電筒功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單手電筒,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • 詳解Android中Handler的實(shí)現(xiàn)原理

    詳解Android中Handler的實(shí)現(xiàn)原理

    這篇文章主要為大家詳細(xì)介紹了Android中Handler的實(shí)現(xiàn)原理,本文深入分析 Android 的消息處理機(jī)制,了解 Handler 的工作原理,感興趣的小伙伴們可以參考一下
    2016-04-04

最新評(píng)論