咱們常常使用LayoutInflater將佈局文件渲染成View層級視圖,那麼具體是怎麼使用的呢?目前有四種方式:android
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rootView = inflater.inflate(R.layout.view_layout, null);
複製代碼
LayoutInflater inflater = LayoutInflater.from(context);
View rootView = inflater.inflate(R.layout.view_layout, null);
複製代碼
LayoutInflater inflater = getLayoutInflater();
View rootView = inflater.inflate(R.layout.view_layout, null);
複製代碼
rootView = View.inflate(context, R.layout.view_layout, null);
複製代碼
經過源碼能夠得知,第2、第3、第四種方式其實就是第一種方式的封裝,最終獲取View實例的是經過inflater實例的inflate()方法。bash
咱們再看一下Activity的onCreate()方法中的setContentView方法:佈局
public void setContentView(int layoutResID) {
...省略
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...省略
}
複製代碼
能夠看到一樣也是使用了inflater的inflate()方法。因此咱們能夠獲得這樣的結論: 不管是咱們本身主動調用inflater的inflate()方法渲染View,仍是Activity經過setContentView來渲染View,都是經過inflater的inflate()方法來完成的。ui
咱們繼續跟蹤inflate()方法,發現最終調用的是inflate(XmlPullParser,ViewGroup, boolean attachToRoot) 方法:spa
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...省略
try {
...省略
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
...省略
}
} catch (XmlPullParserException e) {
..省略
} catch (Exception e) {
...省略
} finally {
...省略
}
return result;
}
}
複製代碼
咱們省略掉了一些代碼,直接從try代碼塊開始看,不考慮merge標籤(其實merge標籤只是正常渲染的一種特殊狀況),咱們就走到了else語句下,關鍵代碼createViewFromTag()生成了View。code
那咱們繼續跟蹤,看一下createViewFromTag執行了什麼操做:xml
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
...省略
try {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
...省略
} catch (Exception e) {
...省略
}
}
複製代碼
咱們仍是講部分代碼省略了,從try代碼塊開始看,當mFactory2或者mFactory不爲null時,View是由它們的onCreateView方法生成的,不然使用系統默認建立View的流程。系統默認建立View的流程是經過判斷標籤名稱有沒有包含".",若是沒有則將前綴"android.view."添加到標籤名前面,最終調用LayoutInflater的createView()方法,而後返回View。 到這裏咱們就知道了LayoutInflater根據佈局文件來渲染View的主要流程:先經過佈局文件的資源ID建立XmlResourceParser解析器對象,而後利用該對象遞歸解析佈局文件,根據解析出來的標籤名生成View,最終返回層級視圖View。而若是LayoutInflater設置了Factory2或者Factory,那麼在建立View時都會調用Factory2或者Factory的onCreateView方法,因此咱們能夠在View建立以前在onCreateView中作一些相關邏輯,好比說換膚。對象
未完待續...遞歸