1. 程式人生 > >[譯]Android view 測量布局和繪制的流程

[譯]Android view 測量布局和繪制的流程

註意 images draw can www -i str 中一 opengl-es

原文鏈接

創造優秀的用戶體驗是我們開發者的主要目標之一.為此, 我們首先要了解系統是如何工作的, 這樣我們才可以更好的與系統配合, 從它的優點中獲益, 規避它的缺陷.


之前關於Android渲染過程的文章

這次我們主要關註Measure/Layout(測量和布局)的階段, 這些階段決定了視圖的大小和位置, 以便於我們能夠繪制它.

技術分享圖片

Step 1: Measure 測量

目標: 確定是圖的大小

視圖的大小包含其子視圖的大小, 且必須符合其父視圖的要求

視圖的大小由2個方面決定:

  • 測量寬度與測量高度 - 視圖在其父視圖中想要占據多大空間.這是此階段我們需要的大小.
  • 寬度與高度(也就是繪制高度與繪制寬度)
    - 在繪制和布局階段, 視圖在屏幕上視圖的大小. 這個大小會在step2中算出.

How does it work?

  • 自頂向下遞歸的遍歷view樹.
  • 每個視圖將規格傳遞給子視圖.

如何實現?父視圖通過MeasureSpec類的三種選項之一來決定子視圖的寬高:

  1. UNSPECIFIED - 未指定, 子視圖可以獲得任意大小
  2. EXACTLY - 指定, 子視圖應該有指定大小
  3. AT_MOST - 最大, 子視圖最大可達到某個值

每個視圖的寬高設置由ViewGroup.LayoutParams的3種選項決定:

  1. 一個明確的數字
  2. MATCH_PARENT 子視圖想要和父視圖一樣大
  3. WRAP_CONTENT
    子視圖想要和自身的內容一樣大
  • 測量過程在onMeasure(int widthMeasureSpec, int heightMeasureSpec)函數中完成

  • 當這個函數返回時, 每個視圖都必須有measuredWidthmeasuredHeight(可由調用super()完成), 否則會拋出IllegalStateException.

Notice that this process sometimes a negotiation between a view and its children, and so measure() may be called more than once. More on that on a later post.

  • 註意這個過程有時需要視圖和其子視圖互相協商, 因此measure()方法可能會被調用不止一次.

Since the traversal is top down, and each parent tells its children the requirements, we end up with our goal achieved:
Each view’s measured size includes its children size, and fit its parent requirements.

因為遍歷是自頂向下的, 並且沒個父視圖告訴子視圖其需求, 我們最終的目標是:
每個視圖的計算大小包含了子視圖的大小, 並符合其父視圖的要求

Step 2: Layout 布局

目標: 為視圖及其子視圖設定位置和大小(繪制寬度和繪制高度)

  • 與step1類似: 自頂向下遞歸遍歷視圖樹.
  • 每個父視圖通過上一個計算的大小定位其所有子視圖的位置.
  • 定位由onLayout(boolean changed, int left, int top, int right, int bottom)方法完成, 其中left, top, right, bottom是相對於其父視圖的.
  • 當重寫onLayout()方法時, 必須調用每個子視圖的layout()方法.

Step 3: Draw 繪制

  • 當大小和位置都確定之後, 視圖可以據此繪制自己.
  • onDraw(Canvas)方法中Canvas對象生成(或者是更新)一系列OpenGl-ES命令(displayList)發送給GPU.

這就是繪制的過程! 但是當我們改變了視圖的屬性時發生了什麽呢? 由動畫, 用戶輸入, 或者我們決定改變他們的時候.


When things change… 當發生改變時

當視圖屬性改變是, 視圖會通知系統. 取決於改變的屬性, 視圖調用下列之一:

  • invalidate - 此時只會調用視圖的onDraw()
  • requestLayout() - 會傳遞到根視圖, 然後調用整個過程(測量->布局->繪制)

對於需要布局的情況有一個經典的小例子: 我們在一個RelativeLayout中有兩個關系相對的視圖. 如果其中一個改變了大小 - 一定會導致另一個重新定位, 也可能導致父視圖改變大小.所以我們改變了一個視圖的屬性, 導致了整個布局都過期了.

技術分享圖片

這種情況提醒我們高效的布局是很重要的, 這樣布局才能流程的執行並且不會導致跳幀.

https://www.cnblogs.com/fortitude/p/8632171.html

[譯]Android view 測量布局和繪制的流程