Android View之布局載入流程

來源:互聯網
上載者:User

標籤:檔案   fill   比較   成員變數   重寫   tin   exception   rip   一個   

 1.引言

最近準備重新學習下Android,加深理解,快速形成自己的知識結構體系。最先學習的就算View部分,從自訂View到Activty階層,到layout載入過程。等等都會看一遍,在此記錄下Layout的載入過程

2.正題2.1 Activity的流程載入

Activity類中setContentView 追蹤(善於用bookMark)

 public void setContentView(@LayoutRes int layoutResID) {        getWindow().setContentView(layoutResID);        initWindowDecorActionBar();    }

追蹤代碼,PhoneWindow的setContentView方法:


 image.png
mLayoutInflater.inflate(layoutResId,mContentParent)

由程式碼片段可以看出來:layoutResId的父布局就是mContentParent。那麼mContentParent究竟是什麼呢?

我們知道DecorView 包括TitleView +ContentView.


 image.png

追蹤代碼:installDescor():

 image.png

 

generateLayout()方法:

protected ViewGroup generateLayout(DecorView decor) {    // Apply data from current theme.    // 獲得表單的 style 樣式    TypedArray a = getWindowStyle();     // 省略大量無關代碼     // Inflate the window decor.    int layoutResource;    int features = getLocalFeatures();     //填充帶有 style 和 feature 屬性的 layoutResource (是一個layout id)     View in = mLayoutInflater.inflate(layoutResource, null);     // 插入的頂層布局 DecorView 中     decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));    mContentRoot = (ViewGroup) in;     // 找到我們XML檔案的父布局 contentParent      ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    if (contentParent == null) {        throw new RuntimeException("Window couldn‘t find content container view");    }    // 省略無關代碼    mDecor.finishChanging();    // 返回 contentParent 並賦值給成員變數 mContentParent    return contentParent;}

給DecorView添加一個名字為mContentRoot的view。並且mContentRoot裡麵包含了一個id為ID_ANDROID_CONTENT的 mContentParent.

得到了mContentParent之後,在根據mLayoutInflater.inflate(layoutResId,mContentParent) 就將Layout布局載入進了DecorView,然後這個視圖才能被我們看到。

驗證:


 image.png

說明了mContentParent是FramLayout

3.AppCompatActivity 載入View得過程
  @Override    public void setContentView(@LayoutRes int layoutResID) {        getDelegate().setContentView(layoutResID);    }

getDelegate():

    @NonNull    public AppCompatDelegate getDelegate() {        if (mDelegate == null) {            mDelegate = AppCompatDelegate.create(this, this);        }        return mDelegate;    }

跟蹤create()方法,

  public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {        return create(activity, activity.getWindow(), callback);    }

activity.getWindow() 由上面可知,得到的肯定是PhoneWindow。問題來了,activity這個時候真的能得到一個非null的PhoneWindow嗎?。發現在activity調用attach方法的時候就會初始化PhoneWindow:

 final void attach(Context context, ActivityThread aThread,            Instrumentation instr, IBinder token, int ident,            Application application, Intent intent, ActivityInfo info,            CharSequence title, Activity parent, String id,            NonConfigurationInstances lastNonConfigurationInstances,            Configuration config, String referrer, IVoiceInteractor voiceInteractor,            Window window, ActivityConfigCallback activityConfigCallback) {        attachBaseContext(context);        mFragments.attachHost(null /*parent*/);        mWindow = new PhoneWindow(this, window, activityConfigCallback);        mWindow.setWindowControllerCallback(this);        ......}

Activity.attach是在performLaunchActivity中本調用,也就是在ActivityThread中被調用。所以在Activity中任何時候getWindow(),都能得到PhoneWindow。

繼續跟蹤代碼:
create方法調用的是:

 private static AppCompatDelegate create(Context context, Window window,            AppCompatCallback callback) {        if (Build.VERSION.SDK_INT >= 24) {            return new AppCompatDelegateImplN(context, window, callback);        } else if (Build.VERSION.SDK_INT >= 23) {            return new AppCompatDelegateImplV23(context, window, callback);        } else if (Build.VERSION.SDK_INT >= 14) {            return new AppCompatDelegateImplV14(context, window, callback);        } else if (Build.VERSION.SDK_INT >= 11) {            return new AppCompatDelegateImplV11(context, window, callback);        } else {            return new AppCompatDelegateImplV9(context, window, callback);        }    }

setContentView 是在AppCompatDelegateImplV9類中被重寫,也就是說最終是調用AppCompatDelegateImplV9.setContentView();

  @Override    public void setContentView(int resId) {        ensureSubDecor();        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);        contentParent.removeAllViews();        LayoutInflater.from(mContext).inflate(resId, contentParent);        mOriginalWindowCallback.onContentChanged();    }
  ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);

很容易讓我們想到mSubDecor 是不是等同於上文中的mContentRoot 呢?

看看ensureSubDecor怎麼寫的:

 if (mOverlayActionMode) {                subDecor = (ViewGroup) inflater.inflate(                        R.layout.abc_screen_simple_overlay_action_mode, null);            } else {                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);            }

subDecor 是系統提供的ViewGroup 裡面有一個android.R.id.content布局,和mContentRoot 作用一模一樣。

基本上分析到此結束,也是比較簡單。

 

Android View之布局載入流程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.