View繪制(一) performTraversals

View的繪制是一個(gè)遞歸的過程,父View繪制自己和子View,然后子view繪制自己和自己的子View。

我們知道遞歸的一般數(shù)學(xué)表達(dá)是A1=T,An=f(An-1),那么與之對應(yīng),View繪制的A1和函數(shù)f()又是什么呢?

答案 :View繪制的A1是DecorView,它是View繪制的起始節(jié)點(diǎn),View繪制的f()函數(shù)是measure,layout,draw三大過程,通過遞歸調(diào)用這三個(gè)方法完成View的繪制。

在介紹View的繪制過程之前,先介紹幾點(diǎn)關(guān)于View的相關(guān)概念,方便大家理解。

一、View相關(guān)概念

屏幕布局層次圖
  1. PhoneWindow:繼承自Window類,負(fù)責(zé)管理界面顯示以及事件響應(yīng),每個(gè)Activity 界面都包含一個(gè)PhoneWindow對象,它是Activity和整個(gè)View系統(tǒng)交互的接口。

  2. DecorView:是PhoneWindow中的起始節(jié)點(diǎn)View,繼承自View類,是作為整個(gè)視圖容器來使用的,主要負(fù)責(zé)設(shè)置窗口屬性。

  3. ViewRoot:在系統(tǒng)啟動(dòng)一個(gè)Activty組件的同時(shí)將其創(chuàng)建,類似于MVC模型中的Controller,負(fù)責(zé)管理、布局和渲染窗口UI等事務(wù)。

二、View繪制開始

View繪制開始
  1. 系統(tǒng)啟動(dòng)一個(gè)Activity的同時(shí)創(chuàng)建一個(gè)ViewRoot實(shí)例。

  2. Activity 在attach階段生成一個(gè)PhoneWindow對象,它包含一個(gè)DecorView對象。

  3. 在Activity執(zhí)行onCreate中的setContentView之后,將讀入的view加載進(jìn)入第一張圖的ContentViews區(qū)域。

  4. 加載完畢后觸發(fā)ViewRoot中的scheduleTraversals異步函數(shù),從而進(jìn)入ViewRoot的performTraversals函數(shù),View的繪制從這里開始。

三、performTraversals

performTraversals

ViewRoot中的performTraversals方法以DecorView為父容器(ViewGroup)開始自上而下的View工作流程。

View的工作流程主要是指measure、layout、draw這三大流程,即測量、布局和繪制,其中measure確定View的測量寬和高,layout確定View的最終寬/高和四個(gè)頂點(diǎn)位置,而draw則將View繪制到屏幕上。

關(guān)于measure,layout,draw的具體內(nèi)容可以看后面的文章,這里不用多在意。這個(gè)函數(shù)的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài),判斷是否重新計(jì)算視圖大小(measure)、是否重新放置視圖的位置(layout)、以及是否重繪 (draw),其核心也就是通過判斷來選擇順序執(zhí)行這三個(gè)方法中的哪個(gè)。

private void performTraversals() {

//1 處理mAttachInfo的初始化,并根據(jù)resize、visibility改變的情況,給相應(yīng)的變量賦值。
  final View host = mView;//這里的mView即DecorView
  final View.AttachInfo attachInfo = mAttachInfo;
  final int viewVisibility = getHostVisibility();
  boolean viewVisibilityChanged = mViewVisibility != viewVisibility
          || mNewSurfaceNeeded;
  float appScale = mAttachInfo.mApplicationScale;
  WindowManager.LayoutParams params = null;
  if (mWindowAttributesChanged) {
      mWindowAttributesChanged = false;
      surfaceChanged = true;
      params = lp;
  }
  Rect frame = mWinFrame;
  if (mFirst) {
      // For the very first time, tell the view hierarchy that it
      // is attached to the window.  Note that at this point the surface
      // object is not initialized to its backing store, but soon it
      // will be (assuming the window is visible).
      attachInfo.mSurface = mSurface;
      attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
              lp.format == PixelFormat.RGBX_8888;
      attachInfo.mHasWindowFocus = false;
      attachInfo.mWindowVisibility = viewVisibility;
      ......
  } 

//2 如果mLayoutRequested判斷為true,那么說明需要重新layout,不過在此之前那必須重新measure。
  if (mLayoutRequested) {
      // Execute enqueued actions on every layout in case a view that was detached
      // enqueued an action after being detached
      getRunQueue().executeActions(attachInfo.mHandler);
      if (mFirst) {
          ......
      } 
  }

//3 判斷是否有子視圖的屬性發(fā)生變化,ViewRoot需要獲取這些變化。
  if (attachInfo.mRecomputeGlobalAttributes) {
      ......
  }
  if (mFirst || attachInfo.mViewVisibilityChanged) {
      ......
  }


//4 根據(jù)上面得到的變量數(shù)值,確定我們的view需要多大尺寸才能裝下。于是就得measure了,有viewgroup的weight屬性還得再做些處理
           // Ask host how big it wants to be
          host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
          mLayoutRequested = true;
      }
  }


//5 measure完畢,接下來可以layout了。
  final boolean didLayout = mLayoutRequested;
  boolean triggerGlobalLayoutListener = didLayout
          || attachInfo.mRecomputeGlobalAttributes;
  if (didLayout) {
      host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);

  }


//6 如果mFirst為true,那么會(huì)進(jìn)行view獲取焦點(diǎn)的動(dòng)作。
  if (mFirst) {
      mRealFocusedView = mView.findFocus();
  }

  boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();


//7 終于,來到最后一步,前面的工作可以說都是鋪墊,都是為了draw而準(zhǔn)備的。
  if (!cancelDraw && !newSurface) {
      mFullRedrawNeeded = false;
      draw(fullRedrawNeeded)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容