LayoutInflater

LayoutInflater 只負責實例化各個 View 類,不負責調用 measure/layout/draw 等方法

經過 inflate() 后,各個 View 的實例都已實例化完成,并且層級關系也已確定。但其 measure/layout/draw 并沒有調用。

因此,onMeasure() 在 onFinishInflate() 后調用。

屬性

  1. sConstructorMap:各個 View 的 Constructor 集合。key 值為各個 View 的 name(自定義的為全名,系統的為 xml 中寫的名,如 TextView等)

  2. mFilter:過濾器。根據 Class 判斷當前 View 是否要過濾。如果在 xml 中使用了要過濾的 View, 則會拋出異常

  3. mFilterMap:存儲過濾結果。key 值為各個 View 的 name (自定義的為全名,系統的為 xml 中寫的名,如 TextView等)

方法

inflate

負責解析出根結點,并根據根結點不同調用下面方法

  1. 當根結點為 merget 時,調用 rInflate()

  2. 當根結點為普通 View 時:

    1. 首先調用 createViewFromTag() 創建根節點自身實例,該方法中并不創建其子 view。
    2. 調用 rInflateChildren() 創建所有子 View,并在創建過程中將子 View 添加到他們各自的父 View 中。

rInflateChildren

其主要用于遞歸創建子 view,同時將子 view 添加到其父布局中。

該方法直接調用 rInflate()。

createViewFromTag

創建 view 自身實例,不涉及其子 view。

該方法中會按 mFactory2,mFactory,mPrivateFactory 優先級創建 View 實例。

            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }
  1. 如果工廠類中有返回值,則本方法結束,將將工廠類的返回值直接返回。

  2. 如果三個工廠類都沒有返回值,就會調用 onCreateView 或 createView —— 系統 view 調用前者,自定義 view 調用后者。

    但 onCreateView 對 name 添加 android.view. 包前綴后,又調用到 createView。

    因此,系統 view 走到 createView 方法時,prefix 不為空,其值為 android.view.;但自定義 view 走到 createView() 時,prefix 為空。

createView

該方法中,經過 mFilter 過濾后,得到對應 view 的 constructor ,然后通過反射得到 view 的實例。

也就是說該方法會得到 view 的實例或者拋出異常(mFilter 沒有過濾通過)


rInflate

遍歷 xml 文件中所有的節點,并創建這些節點對應的 view 實例,同時將每一個實例添加到其父 view 中。

  1. 該方法有兩個入口:

    • 根結點為 merge 標簽時,直接該方法。此時 finishInflate 為 false,且此時 parent 為外界傳入的 root。

    • 通過 rInflateChildren 調用該方法,此時 finishInflate 為 true,且參數 parent 的值為 createViewFromTag() 的返回值。

  2. 該方法內部會通過 while 循環遍歷所有的 view 結點,并通過 parser.getName() 獲取當前結點的名字。拿到名字后,會根據名字的不同進行不同的操作,如:

    • 普通 view,首先調用 createViewFromTag() 生成該 view 實例,再調用 rInflateChildren 創建子 view。最后通過 ViewGroup#addView 將子 View 添加到當前 View 中。

    • merge 標簽:直接拋異常。因為布局的外層 view 已經解析 ( 在 inflate 方法中完成),所以此時出現 merge 標簽說明 merge 并不是最外層。但 merge 只能做為根標簽。

    • tag 標簽:解析 tag 標簽中配置的的 id 與 value,并設置給 parent 。

            <Button
                android:id="@+id/btn"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="選擇圖片">
      
                <tag
                    android:id="@id/id_tag"
                    android:value="test" />
            </Button>
      
    • requestFocus:將當前 parent 設置為焦點 view。

    • include 標簽:直接調用 parseInclude 方法

  1. 該方法最后會調用 view.onFinishInflate()。因此,如果 view 是一個 ViewGroup,該方法回調時表示其子 view 已經添加完畢;如果是一個普通的 View,表示該 View 的實例已創建結束,并且已經 add 到其父 View 中。

parseInclude 方法:

  1. 首先讀取 layout 屬性的值,并嘗試轉成 layoutId(即讀取通過 @layout/layoutId 形式配置的 layout)。如果失敗,會嘗試讀取 theme 中配置的 layout 屬性的值(即通過 ?attr/layout 配置的值)。

  2. 如果上述兩步結束后依舊沒有 layout 的值,則拋異常。

  3. 如果有 layout 的值,則跟普通的通過 inflater() 解析一個 layout 邏輯一樣:首先調用 createViewFromTag 得到布局的根結點實例,再調用 rInflateChildren() 填充所有子 view。

    但在創建根結點實例后,會讀取布局 id 與 visibility 屬性:

    • visibility 控制根結點的顯示與隱藏

    • 會調用 View#setId() 方法,將 id 的值設置為根結點的 id。

    if (id != View.NO_ID) {
          view.setId(id);
    }
    

blink

在解析過程中,有一個 <blink> 標簽,其主要作用是讓其子 View 不斷閃動:出現與隱藏交替進行。

其對應的實例是 LayoutInflater 中的 BlinkLayout 內部類。

    <blink
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="this is test" />
    </blink>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容