Android View (2) View的加載過程

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

View的加載過程

android 在加載視圖的過程是通過在Activity的 setContentView(@LayoutRes int layoutResID) 來加載的,我們通過分析源碼(在線源碼鏈接)過程來理解。

  1. setContentView(int layoutResID)源碼分析
    上篇我們說過WindowPhone類繼承了Window類,Window類只是定義了一些方法和規(guī)范,所以我們直接看WindowPhone類中的方法。有三個(gè)重載方法,我們只分析一個(gè),代碼如下:
  @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        //判斷是否第一次進(jìn)入
        if (mContentParent == null) {
        //創(chuàng)建DecorView,并添加布局到mContentParent
            installDecor();
        //hasFeature(FEATURE_CONTENT_TRANSITIONS)是否設(shè)置了過場動(dòng)畫
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //清空之前的View
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //通過Scene執(zhí)行過場動(dòng)畫(執(zhí)行過程中會(huì)清空之前的View和添加新的布局)
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
        //向mContentParent添加布局
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
        //回調(diào)通知表示完成界面加載
            cb.onContentChanged();
        }
       // Whether the client has explicitly set the content view. If false and mContentParent is notnull, then the content parent was set due to window preservation.
       //是否顯式的設(shè)置了視圖
        mContentParentExplicitlySet = true;
    }

接下來看installDecor() 代碼如下:

       private void installDecor() {
2640        mForceDecorInstall = false;
            // 如果mDecor為空,則生成一個(gè)Decor,并設(shè)置其屬性
2641        if (mDecor == null) {
            // System process doesn't have application context and in that case we need to directly use the     context we have. 
            // Otherwise we want the application context, so we don't cling to the activity.
            //generateDecor()主要用于創(chuàng)建Decor且不依賴與Activity
2642            mDecor = generateDecor(-1);
            //設(shè)置父View 與子View的聚焦關(guān)系
2643            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
           // 設(shè)置mDecor為整個(gè)Activity窗口的根節(jié)點(diǎn),從此處可以看出窗口根節(jié)點(diǎn)為一個(gè)DecorView
2644            mDecor.setIsRootNamespace(true);
          // 滿足條件執(zhí)行動(dòng)畫操作
2645            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
2646                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
2647            }
2648        } else {
2649            mDecor.setWindow(this);
2650        }
2651        if (mContentParent == null) {
              //根據(jù)窗口的風(fēng)格修飾,選擇對應(yīng)的修飾布局文件,并且將id為content的FrameLayout賦值給mContentParent
2652            mContentParent = generateLayout(mDecor);
2653
2654            // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
2655            mDecor.makeOptionalFitsSystemWindows();
2656        //獲取DecorView頂級布局,提供一些設(shè)置title,window主題等的方法
2657            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
2658                    R.id.decor_content_parent);
2659        
2660            if (decorContentParent != null) {
2661              //1. 將decorContentParent賦值給mDecorContentParent 
                      //2. 設(shè)置窗口回調(diào)函數(shù) 
                      //3.設(shè)置窗口的title、icon、logo等屬性值
2699            } else {
2700                mTitleView = findViewById(R.id.title);
                      //這里有一個(gè)我們?nèi)粘J褂玫腁ctionBar是否顯示的判斷,所以我們的設(shè)置要在setContentView方法之前
2701                if (mTitleView != null) {
2702                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
2703                        final View titleContainer = findViewById(R.id.title_container);
2704                        if (titleContainer != null) {
2705                            titleContainer.setVisibility(View.GONE);
2706                        } else {
2707                            mTitleView.setVisibility(View.GONE);
2708                        }
2709                        mContentParent.setForeground(null);
2710                    } else {
2711                        mTitleView.setText(mTitle);
2712                    }
2713                }
2714            }
2715
2716            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
2717                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
2718            }
2719          //....好多關(guān)于背景,動(dòng)畫等的判斷,省略
 2770                }
2771            }
2772        }
2773    }

接下來分析generateLayout(DecorView decor)方法的源碼

//返回當(dāng)前Activity的內(nèi)容區(qū)域視圖,即我們的布局文件顯示區(qū)域mContentParent
protected ViewGroup generateLayout(DecorView decor) {
2309        // Apply data from current theme.
2310        //獲取屬性值
2311        TypedArray a = getWindowStyle();
2312        //根據(jù)Them來設(shè)置或修改各種屬性,選定不同的資源文件等
                .......
                //設(shè)置title和background屬性
2627        }
2628
2629        mDecor.finishChanging();
2630
2631        return contentParent;
2632    }

其實(shí)在geneateLayout中會(huì)根據(jù)我們在清單文件中設(shè)置的主題來進(jìn)行加載相應(yīng)的主題,布局等等。

總結(jié)

  1. 梳理setContentView()加載布局的過程:首先如果是首次調(diào)用的話,我們會(huì)創(chuàng)建DecorView對象作為Activity根視圖,如果不是第一次調(diào)用,我們只需要移除之前的布局,繼續(xù)用DecorView對象去作為根視圖, 在加載布局過程中會(huì)通過設(shè)置的ThemStyle 設(shè)置一些屬性值,然后通過findviewbyid的方式將跟布局文件添加到DecorView中,然后通過回調(diào)Activity的onContentChanged方法,通知布局加載完成
  2. 在加載View的過程中,系統(tǒng)是通過遞歸遍歷一步步的進(jìn)行解析加載的,所以我們在開發(fā)中,盡量不要嵌套太多層的布局,會(huì)影響到布局的加載效率,可以通過merge標(biāo)簽來減少嵌套層數(shù)。

參考

  1. 源碼:http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/
  2. 扔物線 博客:http://hencoder.com/
  3. 簡書:
    http://www.itdecent.cn/p/f76a6f6f75aa setContentView 加載視圖機(jī)制
    http://www.itdecent.cn/p/0819a858b53c setContentView 源碼分析
    http://www.itdecent.cn/p/bb7977990baa android view繪制流程,源碼解讀
    http://www.itdecent.cn/p/b272528165a2 android 自定義view
  4. ColorFilter:https://github.com/chengdazhi/StyleImageView(自定義view的玩法)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容