LayoutInflater#inflate 源碼解析

LayoutInflater 是系統提供的一個解析工具,用於將佈局XML文件實例化爲其相應的 View 對象。

調用流程

LayoutInflater 有個多個重載的inflate 方法:

android.view.LayoutInflater#inflate(int, android.view.ViewGroup)
android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)
android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup)
android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean)

最後都會執行到 android.view.LayoutInflater#inflate(org.xmlpull.v1.XmlPullParser, android.view.ViewGroup, boolean) 方法內。
另外android.view.View#inflate 最終也是調用的LayoutInflater的inflater 方法
在這裏插入圖片描述
附一個整體的調用流程圖
在這裏插入圖片描述

源碼分析

比較常用的是
android.view.LayoutInflater#inflate(int, android.view.ViewGroup)
android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)
這兩個方法,那麼這個root 是幹嘛的,attachToRoot 的作用是啥?我們從源碼中去尋找答案。
(*注:sdk版本爲28)

/**
 1. 將 xml節點解析成一個view 對象
 2. <p>
 3.  4. @param parser XML dom node containing the description of the view
 5.        hierarchy.
 6. @param root 根view,如果attachToRoot 爲true,則將xml 解析得到的view 添加到此view 上。
          如果false,則爲xml解析得到的view 提供LayoutParams,並不會附加到該view 上。
 7. @param attachToRoot 是否將xml 解析得到的view 添加到root view上。
          如果爲false,則root view 僅用作爲xml 中的頂視圖創建LayoutParmas。
 8. @return 如果提供的 root 不爲null 並且attachToRoot爲true,則返回 root;否則返回解析xml得到的根view。
 */
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        // --忽略 解析相關 代碼--
        
        View result = root;

        try {
            // --忽略 解析相關 代碼--

            final String name = parser.getName();

            // --忽略 debug 代碼--

            // merge 標籤,繼續向下遞歸解析
            if (TAG_MERGE.equals(name)) {
                // merge 標籤必須有root,並attachToRoot爲true,否則拋異常
                if (root == null || !attachToRoot) {
                    throw new InflateException("<merge /> can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }
                // 遞歸方法,用於向下延伸xml層次結構並實例化視圖、實例化其子視圖
                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // 找出xml中的根view,賦給temp。注意,此根視圖和root 不是同一個!
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;

                if (root != null) {
                    // -- 忽略debug 代碼
                    
                    // 創建root 的LayoutParams
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // 如果 attachToRoot爲false,給temp 設置layoutParams。                     
                        temp.setLayoutParams(params);
                    }
                }
                
                // -- 忽略debug 代碼

                // 解析temp 下的所有子視圖
                rInflateChildren(parser, temp, attrs, true);
                
                // -- 忽略debug 代碼

                // 如果設置了root 並且attachToRoot 爲 true,則添加view 到根視圖
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

                // 如果root 爲空或者 attachToRoot爲false,返回在xml中找到的頂視圖。否則返回傳入的根視圖。
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

        } catch (XmlPullParserException e) {
            final InflateException ie = new InflateException(e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } catch (Exception e) {
            final InflateException ie = new InflateException(parser.getPositionDescription()
                    + ": " + e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        return result;
    }
}

簡單總結下流程:

  1. 通過XmlPullParser 解析xml 佈局,獲取佈局中的根視圖 temp;
  2. 根據 root 是否爲null 來決定是否爲temp 創建LayoutParams;
  3. 根據 root 是否爲null 及 attachToRoot 是否爲true 來決定是否將 temp 添加到root 上;
  4. 返回正確的view。如果temp 成功添加到root 上,返回 root;否則返回temp。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章