Android-View的繪製源碼學習總結

來源:互聯網
上載者:User

標籤:載入布局   google   match   content   繪製   canvas   編碼   http   內容   

##前言

算是第一篇正式的github博文,回顧了一下之前看過的view源碼解析,做一個對目前為止View學習小的總結。

我覺得對於源碼的解析和學習,把所有流程記下來意義並不是很大,最關鍵的是:

1.知道基本作用和用法
2.大概瞭解整個流程和實現方法
3.瞭解裡面可擴充的地方在哪,更靈活地使用
4.整個源碼設計和細節有沒有什麼亮點值得參考和學習
5.源碼設計的思路

這也是寫這篇文章的目的所在。

##載入布局

####LayoutInflater

http://www.cnblogs.com/qlky/p/5674975.html

- **作用**

LayoutInflater是用來載入布局的,我們最簡單的代碼:

```
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
}
```

中的setContentView就是用LayoutInflater實現的

- **用法**

可以用來動態載入布局
```
LayoutInflater layoutInflater = LayoutInflater.from(this);
View buttonlayout = layoutInflater.inflate(R.layout.button_layout,null);
mainlayout.addView(buttonlayout);
```

- **原理**

inflate是用pull來解析xml格式的,其中調用了createViewFromTag()這個方法,並把節點名和參數傳了進去。看到這個方法名,我們就應該能猜到,它是用於根據節點名來建立View對象的。
確實如此,在createViewFromTag()方法的內部又會去調用createView()方法,然後使用反射的方式建立出View的執行個體並返回。

對於布局子項目會用rinflate()去迴圈實現。

- **拓展**

從源碼可以知道

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)的第三個參數attachToRoot

1. 如果root為null,attachToRoot將失去作用,設定任何值都沒有意義。

2. 如果root不為null,attachToRoot設為true,則會給載入的布局檔案的指定一個父布局,即root。

3. 如果root不為null,attachToRoot設為false,則會將布局檔案最外層的所有layout屬性進行設定,當該view被添加到父view當中時,這些layout屬性會自動生效。

4. 在不設定attachToRoot參數的情況下,如果root不為null,attachToRoot參數預設為true。


所有控制項的layout_width等屬性,都要有一個父布局才會生效,因為這是用來設定view在布局中的大小,而不是view的大小,所以叫layout_width,不是width。

而LinearLayout的layout_width有效是因為,在setContentView()方法中,Android會自動在布局檔案的最外層再嵌套一個FrameLayout,id為content

所以叫setContentView(),其實這個方法也是用Layoutlnflater()實現的

 

##視圖繪製

http://www.cnblogs.com/qlky/p/5676578.html
http://www.jianshu.com/p/5a71014e7b1b

View的繪製要經過三個過程,measure,layout和draw


####Measure

- **作用**

測量view及其子view的大小


- **原理**

MeasureSpec:由父View的MeasureSpec和子View的LayoutParams通過簡單的計算得出一個針對子View的測量要求

計算原理很簡單:
如果我們在xml 的layout_width或者layout_height 把值都寫死,那麼上述的測量完全就不需要了,之所以要上面的這步測量,是因為 match_parent 就是充滿父容器,wrap_content 就是自己多大就多大, 我們寫代碼的時候特別爽,我們編碼方便的時候,google就要幫我們計算你match_parent的時候是多大,wrap_content的是多大,這個計算過程,就是計算出來的父View的MeasureSpec不斷往子View傳遞,結合子View的LayoutParams 一起再算出子View的MeasureSpec,然後繼續傳給子View,不斷計算每個View的MeasureSpec,子View有了MeasureSpec才能更測量自己和自己的子View。


幾種情況:
1.父view MeasureSpec = exactly 大小確定,子view:
match_parent:EXACTLY
warp_content:AT MOST
確定值

2.父view MeasureSpec = exactly 大小不確定,子view:
match_parent:AT MOST
warp_content:AT MOST
確定值


View的測量主要在onMeasure方法裡

對於View預設是測量很簡單,大部分情況就是拿計算出來的MeasureSpec的size 當做最終測量的大小。而對於其他的一些View的衍生類別,如TextView、Button、ImageView等,它們的onMeasure方法系統了都做了重寫,不會這麼簡單直接拿 MeasureSpec 的size來當大小,而去會先去測量字元或者圖片的高度等,然後拿到View本身content這個高度(字元高度等),如果MeasureSpec是AT_MOST,而且View本身content的高度不超出MeasureSpec的size,那麼可以直接用View本身content的高度(字元高度等),而不是像View.java 直接用MeasureSpec的size做為View的大小。


整個View的Root是DecorView,那麼View的繪製是從哪裡開始的呢,我們知道每個Activity 均會建立一個 PhoneWindow對象,是Activity和整個View系統互動的介面,每個Window都對應著一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯絡,對於Activity來說,ViewRootImpl是串連WindowManager和DecorView的紐帶,繪製的入口是由ViewRootImpl的performTraversals方法來發起Measure,Layout,Draw等流程的。

具體流程: DecorView -> ... -> content -> LinearLayout/... -> TextView/...
得到每層的MeasureSpec,最後在底層得到大小。然後再往上得到父view的大小


- **拓展**

對於onMeasure方法的重寫

####Layout

- **作用**

確定view在布局中的位置。ViewRoot的performTraversals()方法會在measure結束後繼續執行,並調用View的layout()方法來執行此過程,如下所示:

```
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
```

- **原理**

首先會調用setFrame()方法來判斷視圖的大小是否發生過變化,以確定有沒有必要對當前的視圖進行重繪,同時還會在這裡把傳遞過來的四個參數分別賦值給mLeft、mTop、mRight和mBottom這幾個變數。
接下來會調用onLayout()方法。
View中的onLayout()方法是一個空方法,因為onLayout()過程是為了確定視圖在布局中所在的位置,而這個操作應該是由布局來完成的,即父視圖決定子視圖的顯示位置。
ViewGroup中的onLayout()方法是一個抽象方法,子類必須重載onLayout函數,而重載onLayout的目的就是安排其children在父視圖的具體位置


- **拓展**

重載onLayout函數


####Draw

- **作用**
繪製view


- **原理**

分為六步,其中第二步和第五步在一般情況下很少用到

第一步:背景繪製

第三步,對View的內容進行繪製
onDraw(canvas) 方法是view用來draw 自己的,具體如何繪製,顏色線條什麼樣式就需要子View自己去實現,View.java 的onDraw(canvas) 是空實現,ViewGroup 也沒有實現,每個View的內容是各不相同的,所以需要由子類去實現具體邏輯。

第四步 對當前View的所有子View進行繪製
dispatchDraw(canvas) 方法是用來繪製子View的,View.java 的dispatchDraw()方法是一個空方法,因為View沒有子View,不需要實現dispatchDraw ()方法,ViewGroup就不一樣了,它實現了dispatchDraw ()方法,就是遍曆子View然後drawChild(),drawChild()方法實際調用的是子View.draw()方法,ViewGroup類已經為我們實現繪製子View的預設過程,這個實現基本能滿足大部分需求,所以ViewGroup類的子類(LinearLayout,FrameLayout)也基本沒有去重寫dispatchDraw方法,我們在實現自訂控制項,除非比較特別,不然一般也不需要去重寫它

第6步 對View的捲軸進行繪製


- **拓展**

重寫onDraw方法

Android-View的繪製源碼學習總結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.