Android View (3) View LayoutInflater源碼分析

本系列文章循序漸進的學習Android View的使用和核心源碼分析。
Android View (1) View的樹形結(jié)構(gòu)和坐標計算
Android View (2) View的加載過程
Android View (3) View LayoutInflater 源碼分析
Android View (4) View的繪制過程

LayoutInflater 布局加載

android LayoutInflater 的最常見的使用是在代碼中動態(tài)的加載布局兑巾,比如ListView厦章、RecyclerView等灭衷,在我上一篇介紹Activity View加載過程中垮衷,也是通過LayoutInflater來加載XML布局的冈闭,今天就從源碼的角度稼稿,分析他的加載過程。

LayoutInflater初始化

LayoutInflater的初始化有兩種方式
1.LayoutInflater layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);通過Context來獲取
2.LayoutInflater layoutInflater = LayoutInflater.from(context);通過自身的from方法獲取者吁,這種方式只是對上面的代碼進行了封裝而已窘俺,from源碼如下:

225     * Obtains the LayoutInflater from the given context.
226     */
227    public static LayoutInflater from(Context context) {
228        LayoutInflater LayoutInflater =
229                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
230        if (LayoutInflater == null) {
231            throw new AssertionError("LayoutInflater not found.");
232        }
233        return LayoutInflater;
234    }

LayoutInflater的使用

LayoutInflater加載布局的方法是layotInflater.inflate(),該方法有四個重載方式:

 inflate(@LayoutRes int resource, @Nullable ViewGroup root)
 inflate(XmlPullParser parser, @Nullable ViewGroup root) 
 inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 
 inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

最終都是走的最后一個方法,所以我們只需要分析最后一個方法的源碼:

        public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
452        synchronized (mConstructorArgs) {
453            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
454
455            final Context inflaterContext = mContext;
456            final AttributeSet attrs = Xml.asAttributeSet(parser);
457            Context lastContext = (Context) mConstructorArgs[0];
458            mConstructorArgs[0] = inflaterContext;
                  //傳入父View
459            View result = root;
460
461            try {
462                // Look for the root node.
463                int type;
                  //確定根節(jié)點
464                while ((type = parser.next()) != XmlPullParser.START_TAG &&
465                        type != XmlPullParser.END_DOCUMENT) {
466                    // Empty
467                }
468            //驗證XML是否正確
469                if (type != XmlPullParser.START_TAG) {
470                    throw new InflateException(parser.getPositionDescription()
471                            + ": No start tag found!");
472                }
473           //獲取布局的名稱
474                final String name = parser.getName();
475
476                if (DEBUG) {
477                    System.out.println("**************************");
478                    System.out.println("Creating root view: "
479                            + name);
480                    System.out.println("**************************");
481                }
482                
483                if (TAG_MERGE.equals(name)) {
                      //處理merge相關的操作
484                    if (root == null || !attachToRoot) {
485                        throw new InflateException("<merge /> can be used only with a valid "
486                                + "ViewGroup root and attachToRoot=true");
487                    }
488                //遞歸inflate方法
489                    rInflate(parser, root, inflaterContext, attrs, false);
490                } else {
                          //通過createViewFromTag傳入節(jié)點和參數(shù)名創(chuàng)建view實例(過程是在createViewFromTag()方法的內(nèi)部又會去調(diào)用createView()方法复凳,然后使用反射的方式創(chuàng)建出View的實例并返回)
491                    // Temp is the root view that was found in the xml
492                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
493
494                    ViewGroup.LayoutParams params = null;
495
496                    if (root != null) {
497                        if (DEBUG) {
498                            System.out.println("Creating params from root: " +
499                                    root);
500                        }
501                        // Create layout params that match root, if supplied
                              //根據(jù)root創(chuàng)建LayoutParams
502                        params = root.generateLayoutParams(attrs);
503                        if (!attachToRoot) {
504                            // Set the layout params for temp if we are not
505                            // attaching. (If we are, we use addView, below)
506                            temp.setLayoutParams(params);
507                        }
508                    }
509
510                    if (DEBUG) {
511                        System.out.println("-----> start inflating children");
512                    }
513
514                    // Inflate all children under temp against its context.
                          //遞歸inflate剩下的chrildren,最終調(diào)用rInflate下面會分析
515                    rInflateChildren(parser, temp, attrs, true);
516
517                    if (DEBUG) {
518                        System.out.println("-----> done inflating children");
519                    }
520
521                    // We are supposed to attach all the views we found (int temp)
522                    // to root. Do that now.
                          //root非空且attachToRoot=true則將xml文件加載到root
523                    if (root != null && attachToRoot) {
524                        root.addView(temp, params);
525                    }
526
527                    // Decide whether to return the root that was passed in or the
528                    // top view found in xml.
                          //將布局文件最外層的所有l(wèi)ayout屬性進行設置瘤泪,當該view被添加到父view當中時,這些layout屬性會自動生效
529                    if (root == null || !attachToRoot) {
530                        result = temp;
531                    }
532                }
533
534            } catch (XmlPullParserException e) {
535                final InflateException ie = new InflateException(e.getMessage(), e);
536                ie.setStackTrace(EMPTY_STACK_TRACE);
537                throw ie;
538            } catch (Exception e) {
539                final InflateException ie = new InflateException(parser.getPositionDescription()
540                        + ": " + e.getMessage(), e);
541                ie.setStackTrace(EMPTY_STACK_TRACE);
542                throw ie;
543            } finally {
544                // Don't retain static reference on context.
545                mConstructorArgs[0] = lastContext;
546                mConstructorArgs[1] = null;
547
548                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
549            }
550            //返回參數(shù)root或xml文件里的root
551            return result;
552        }
553    }

接下來分析rInflate 遞歸inflate的代碼:

  /**
828     * Recursive method used to descend down the xml hierarchy and instantiate
829     * views, instantiate their children, and then call onFinishInflate().
830     * <p>
831     * <strong>Note:</strong> Default visibility so the BridgeInflater can
832     * override it.
833     */
834    void rInflate(XmlPullParser parser, View parent, Context context,
835            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
836
837        final int depth = parser.getDepth();
838        int type;
839        boolean pendingRequestFocus = false;
840        //android 自帶Pull解析XML模式開啟
841        while (((type = parser.next()) != XmlPullParser.END_TAG ||
842                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
843            //找到start_tag節(jié)點
844            if (type != XmlPullParser.START_TAG) {
845                continue;
846            }
847            //獲取Name標記
848            final String name = parser.getName();
849            //處理requestFocus
850            if (TAG_REQUEST_FOCUS.equals(name)) {
851                pendingRequestFocus = true;
852                consumeChildElements(parser);
                   //處理tag
853            } else if (TAG_TAG.equals(name)) {
854                parseViewTag(parser, parent, attrs);
                   //處理include
855            } else if (TAG_INCLUDE.equals(name)) {
856                if (parser.getDepth() == 0) {
857                    throw new InflateException("<include /> cannot be the root element");
858                }
859                parseInclude(parser, context, parent, attrs);
                  //處理merge merge需要是xml中的根節(jié)點
860            } else if (TAG_MERGE.equals(name)) {
861                throw new InflateException("<merge /> must be the root element");
862            } else {
863                final View view = createViewFromTag(parent, name, context, attrs);
864                final ViewGroup viewGroup = (ViewGroup) parent;
865                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
866                rInflateChildren(parser, view, attrs, true);
867                viewGroup.addView(view, params);
868            }
869        }
870
871        if (pendingRequestFocus) {
872            parent.restoreDefaultFocus();
873        }
874        //處理完成育八,回調(diào)onFinishInflate
875        if (finishInflate) {
876            parent.onFinishInflate();
877        }
878    }

分析完成后对途,LayoutInflater其實就是使用Android提供的pull解析方式來解析布局文件的,通過遞歸的方式一層層的解析髓棋。

參考

1.郭霖:http://blog.csdn.net/guolin_blog/article/details/12921889
2.簡書:http://www.reibang.com/p/a66da642b3e2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末实檀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子按声,更是在濱河造成了極大的恐慌膳犹,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件签则,死亡現(xiàn)場離奇詭異须床,居然都是意外死亡,警方通過查閱死者的電腦和手機渐裂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門豺旬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柒凉,你說我怎么就攤上這事哈垢。” “怎么了扛拨?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長举塔。 經(jīng)常有香客問我绑警,道長,這世上最難降的妖魔是什么央渣? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任计盒,我火速辦了婚禮,結(jié)果婚禮上芽丹,老公的妹妹穿的比我還像新娘北启。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布咕村。 她就那樣靜靜地躺著场钉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪懈涛。 梳的紋絲不亂的頭發(fā)上逛万,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音批钠,去河邊找鬼宇植。 笑死,一個胖子當著我的面吹牛埋心,可吹牛的內(nèi)容都是我干的指郁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼拷呆,長吁一口氣:“原來是場噩夢啊……” “哼闲坎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洋腮,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤箫柳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后啥供,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悯恍,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年伙狐,在試婚紗的時候發(fā)現(xiàn)自己被綠了涮毫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡贷屎,死狀恐怖罢防,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唉侄,我是刑警寧澤咒吐,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站属划,受9級特大地震影響恬叹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜同眯,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一绽昼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧须蜗,春花似錦硅确、人聲如沸目溉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缭付。三九已至,卻和暖如春大莫,著一層夾襖步出監(jiān)牢的瞬間蛉腌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工只厘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烙丛,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓羔味,卻偏偏與公主長得像河咽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赋元,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355