這里開啟一輪關(guān)于View的研究肉津,主要選擇View開始研究的原因是我愿意(? ??_??)?,碴开!在深入了解View的過程中桥嗤,主要有兩個目的咨堤,其一是整理一下以前對于View的認(rèn)識且糾正一下錯思維誤區(qū),其二是更深入了解其運(yùn)行原理以備日后能夠侃侃而談騙基友顿涣。
下面進(jìn)入正題:setContentView()
首先從最宏觀的角度來說波闹,我們在學(xué)習(xí)android的過程中第一次遇到關(guān)于View方法就是setContentView(),隨著學(xué)習(xí)的加深涛碑,我們發(fā)現(xiàn)當(dāng)代碼中要實例化一個View的時候精堕,很多時候是通過LayoutInflater來進(jìn)行的,那為什么setContentView()不需要通過這種方式蒲障?
為啥不需要歹篓,就是我們這篇文章要研究的內(nèi)容,so read the fucking code揉阎。
---- Acvtivity.java ----
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
?
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
....
}
上述代碼來源于最基礎(chǔ)的Activity類庄撮,我們從最基本的看起即可,在代碼可以看到主要做了兩個操作:
獲取window進(jìn)行設(shè)置contentView
初始化ActionBar
從上方我們可以推測毙籽,初始化contentView的過程洞斯,其實是在window中進(jìn)行的,而這個window的具體實現(xiàn)則是PhoneWindow,在Activity的attach方法中我們可以知道坑赡,因此需要進(jìn)入的PhoneWindow中過去查看setContentView():
---- PhoneWindow.java ----
?
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//初始化DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
?
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//調(diào)用inflate初始化
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
在上述代碼中FEATURE_CONTENT_TRANSITIONS是5.0增加的一個標(biāo)識來識別是否進(jìn)行實現(xiàn)Activity或者Fragment切換時的異常復(fù)雜的動畫效果烙如,我們可以省略來進(jìn)行代碼分析:
installDecor()扭仁,初始化DecorView以及mContentParent操作
調(diào)用LayoutInflater.inflate()方法初始化布局
---- PhoneWindow.java ----
?
private void installDecor() {
if (mDecor == null) {
//此方法new了一個DecorView
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
//進(jìn)行mContentParent初始化的過程
mContentParent = generateLayout(mDecor);
?
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
...
}
在DecorView和mContentParent初始化完成后,系統(tǒng)再調(diào)用inflate()方法來進(jìn)行布局的初始化的過程厅翔,我們需要知道具體是哪個LayoutInflater來完成工作的:
// PhoneWindow.java ----
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
//LayoutInflater.java ----
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
//ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
//SystemServiceRegistry
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
我們看到inflater的初始化來自于from方法,這在我們擼代碼的時候其實也經(jīng)常會那么寫搀突,但是點到進(jìn)去會發(fā)現(xiàn)是個抽象類刀闷,而inflater由context.getSystemService()獲取,由于context的具體實現(xiàn)類是ContextImpl仰迁,我們再一次追溯甸昏,知道在SystemServiceRegistry中找到了具體實現(xiàn)類:PhoneLayoutInflater。
接著我們回溯到PhoneWindow的mLayoutInflater.inflate(layoutResID, mContentParent)徐许,其實到這步就已經(jīng)跟我們在布局中使用LayoutInflater的效果一樣了施蜜。
but,我們要有好奇心雌隅,點進(jìn)去看看我們跳回inflate()這個方法,跳回PhoneLayoutInflater中進(jìn)行查看翻默,發(fā)現(xiàn)里面并沒有inflate方法,那只能重新回到LayoutInflater.java中了恰起,發(fā)現(xiàn)找了半天并沒有什么卵用修械,不過了解到LayoutInlfater的具體實現(xiàn)是PhoneLayoutInflater對我們也是很有幫助的= ̄ω ̄=。
//LayoutInflater.java
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
//獲取資源文件
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//獲取xml解析器XmlResourceParser
final XmlResourceParser parser = res.getLayout(resource);
try {
//返回解析后得到的View
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
```
最終還是比較簡單的检盼,我們在inflate中看到肯污,其實獲得XmlResourceParser解析器,然后將布局文件添加到了根布局當(dāng)中吨枉。
等等最后還有一個方法:
```java
//PhoneWindow.java
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
```
這個callback是干哈用的蹦渣,點進(jìn)去:
```java
public void setCallback(Callback callback) {
mCallback = callback;
}
?
/**
* Return the current Callback interface for this window.
*/
public final Callback getCallback() {
return mCallback;
}
```
我們只能找到setCallBack()這個方法的具體位置,我們在上面的activity找到了window=new PhoneWindow()這個實現(xiàn):
```java
//activity
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
....
}
```
mWindow.setCallback(this)這句話是不是夠了貌亭!嗯哼柬唯!那么 cb.onContentChanged()這個方法其實就是調(diào)用了在activity中的onContentChanged方法,這是通知ContentView發(fā)生改變的方法圃庭,我們可以重寫進(jìn)行一些你喜歡的操作权逗。
----
####總結(jié)
畫張圖吧,這樣比較好理解:
![zzzz.png](http://upload-images.jianshu.io/upload_images/1838912-699d1a9f08cc5789.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-----
如果您發(fā)現(xiàn)哪里寫的不對冤议,麻煩您指出一下斟薇,我會給您一個么么噠,希望一起進(jìn)步恕酸!
祝好~