前言
感覺很長時(shí)間沒寫文章了蚀狰,這個(gè)星期因?yàn)榛丶液吞幚眄?xiàng)目問題,還是花了很多時(shí)間的歇僧。雖然知道很多東西如果只是看一下用一次,很快就會(huì)遺忘锋拖,但認(rèn)認(rèn)真真地做輸出還是需要一定恒心的诈悍。
這次寫 LayoutInflater 的工作流程,是由于小組一位成員在調(diào)用inflate 方法時(shí)兽埃,沒有傳入 parent 參數(shù)導(dǎo)致生成的布局寬高失效的問題侥钳。
這里先說原因,是因?yàn)槿绻?inflate 的 View柄错,沒有包含在某個(gè) Viewgroup 下舷夺,也沒有傳入 parent 參數(shù),那么他的 layout_width 等屬性就會(huì)失效售貌,這些屬性是需要 View 處在某個(gè)布局下才能生效给猾。
使用
獲取 LayoutInflater
通過 LayoutInflater layoutInflater = LayoutInflater.from(context) 就可以獲取到 LayoutInflater。
如果調(diào)用 View.inflate 也是先會(huì)獲取 LayoutInflater颂跨,再使用 LayoutInflater 去加載布局敢伸。獲取 LayoutInflater 的源碼如下:
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;
}
可以看到,我們其實(shí)也可以自己去調(diào)用 getSystemService 來獲取 LayoutInflater恒削。
加載布局
接下來池颈,使用 layoutInflater.inflate 方法即可加載相應(yīng)布局尾序,有四個(gè)重載方法如下圖:
其中 parser 參數(shù)是一個(gè)描述 View 層次的 XML 文件,在 Android 中我們就是用 XML 來描述布局的躯砰,所以直接傳入布局的 int 值就可以了每币。
另外兩個(gè)需要注意的參數(shù)是 root 和 attachToRoot,root 是指定了本次加載布局的父容器琢歇,attachToRoot 則表示是否將本次加載的內(nèi)容添加到父容器中兰怠。
我們常常會(huì)碰到在 adapter 中,設(shè)置 attachToRoot 為 true 時(shí)會(huì)報(bào)錯(cuò)矿微,是因?yàn)?adapter 會(huì)自己將加載的布局添加到父容器里痕慢,如果自己設(shè)置的話尚揣,就會(huì)導(dǎo)致重復(fù)添加了涌矢。
工作流程
所有的 inflate 到最后都會(huì)返回到下面這個(gè)方法中來進(jìn)行布局加載:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
...
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);
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
代碼較多,我去掉了有關(guān) DEBUG 的部分快骗,同時(shí)最后異常處理的部分也可以略過不看娜庇,那么邏輯就比較清晰了。LayoutInflater 使用 XmlPullParser 來解析布局文件方篮,首先根據(jù)我們傳入的布局參數(shù)創(chuàng)建根布局:
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
這個(gè) params 參數(shù)是用來為 temp 即加載的根布局進(jìn)行屬性參數(shù)設(shè)置的名秀,他有以下三種情況:
ViewGroup root 參數(shù)為 null,則返回加載的布局不做其他設(shè)置
-
root != null && attachToRoot == null藕溅,則使用父布局的參數(shù)對 temp 進(jìn)行設(shè)置
params = root.generateLayoutParams(attrs); temp.setLayoutParams(params);
-
root != null && attachToRoot != null匕得,則將加載的布局添加到父布局,再返回父布局:
root.addView(temp, params);
接下來還會(huì)遍歷加載根布局的子元素:
rInflateChildren(parser, temp, attrs, true);
這樣就完成了一次布局的加載巾表,具體是如何加載的汁掠,就得去學(xué)習(xí) View 的工作原理了。
參考資料
這里也提示一下集币,你看到的資料不一定都是最正確的考阱,盡量接受多方的信息,比如一篇博客看完后鞠苟,最好是能把評論也翻看一遍乞榨。