Android:TextView的Layout創建過程

TextView組件,使用頻率非常高,雖然使用起來比較簡單,但是它的實現其實是一個複雜的過程。它對文字的管理是通過

Layout實現的。TextView內部會根據不同的設置,創建不同的Layout,總共有三種。

DynamicLayout:用在EditText或者TextView中設置的是Spannable類型的文字(比如ClickSpanURL等)。

BoringLayout:常用在處理單行文本。(所有字符必須是LTR[left-to-right]

StaticLayout:這個是默認的TextViewLayout,用在文字不會被改變的狀態下。

TextView中有一個Layout類型的屬性。

<code>private Layout mLayout;/<code>

那麼它是在什麼時候被創建的呢?我們就從measure說起。

<code>if (mLayout == null) {
makeNewLayout(want, hintWant, boring, hintBoring,
width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
}/<code>

measure方法中,會判斷layout是否已經存在,如果不存在,就會通過makeNewLayout來創建。

<code>/**
* @param wantWidth 組件佈局需要的寬度
* @param hintWidth hint的寬度
* @param boring 組件的度量信息
* @param hintBoring hint的度量信息
* @param ellipsisWidth 帶上省略號需要的寬度,這個不包括左右的padding
* @param bringIntoView 如果設置為true,會註冊一個繪製前的回調,
* 在view的tree結構被繪製前觸發onPreDraw
*/
protected void makeNewLayout(int wantWidth, int hintWidth,
BoringLayout.Metrics boring,
BoringLayout.Metrics hintBoring,
int ellipsisWidth, boolean bringIntoView)/<code>

這個方法中又通過makeSingleLayout創建

layout

<code>mLayout = makeSingleLayout(wantWidth, boring, ellipsisWidth, alignment,
shouldEllipsize,effectiveEllipsize,
effectiveEllipsize == mEllipsize);/<code>

shouldEllipsize表示如果蚊子太多,顯示不下,是否處理省略號的顯示。

<code>boolean shouldEllipsize = mEllipsize != null && getKeyListener() == null;

//對於非編輯組件,getKeyListener一般為null/<code>

如果你設置了mEllipsize屬性(省略號顯示的位置)並且key的監聽存在,那就是true

<code>protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
boolean useSaved) {
Layout result = null;
//對於Spannable類型的,創建動態佈局
if (mText instanceof Spannable) {
result = new DynamicLayout(......);
} else {
if (boring == UNKNOWN_BORING) {
//檢查是否可以用BoringLayout
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
......
}
//如果boring不為空,那麼就要把測量結果和實際的顯示區域進行比較,來確定是否可以單行顯示
if (boring != null) {

//如果測量寬度不大於需要的寬度,
//並且設置了省略號位置或者測量寬度不大於帶省略號時的寬度
if (boring.width <= wantWidth
&& (effectiveEllipsize == null || boring.width <= ellipsisWidth)) {
......
}
//或者需要處理省略號,並且寬度滿足
else if (shouldEllipsize && boring.width <= wantWidth) {
......
}
}
}
//如果上面的條件都不滿足,就會創建StaticLayout
if (result == null) {
//創建StaticLayout
StaticLayout.Builder builder = StaticLayout.Builder.obtain(......);
//設置省略號的顯示樣式及寬度
if (shouldEllipsize) {
builder.setEllipsize(effectiveEllipsize)
.setEllipsizedWidth(ellipsisWidth);
}
result = builder.build();
}
return result;
}/<code>

首先,先判斷組件文本是否符合Spannable類型,如果是,則創建動態佈局DynamicLayout

否則,通過BoringLayout.isBoring進行測量。意思就是,如果這些文字都顯示在一行中,那麼需要多大的顯示區域,會把

Metrics類型的度量數據返回來。有了這個數據,那麼接著就要用測量數據和實際佈局中的顯示區域進行比較,看看能否容納測量結果。

如果可以,那麼就返回這個 BoringLayout

如果放不下,就認為不能使用BoringLayout佈局進行顯示了,那就創建一個靜態佈局。

好了,這樣Layout就創建好了,這裡是大概的流程,裡面具體的實現,後面會繼續分析。


分享到:


相關文章: