關於inflate的幾個方法解析(結合日誌原始碼)
阿新 • • 發佈:2018-11-29
inflate使我們使用頻率極高的api了,並且他有多個過載的方法,如下:
View inflate(int, ViewGroup)
View inflate(XmlPullParser, ViewGroup)
View inflate(int, ViewGroup, boolean)
View inflate(XmlPullParser, ViewGroup, boolean)
我們要在不同的使用場景下,進行介紹。
- 我們一般不使用傳入XmlPullParser解析器的方法,一般都是直接傳入XML檔案,方法內部會將XML轉換成解析器,程式碼如下:
final XmlResourceParser parser = res.getLayout(resource);
- 剩餘的兩個方法主要是最後一個引數(attachToRoot)是否傳入的區別,其實兩個引數的方法,最終會呼叫到三個引數的方法,程式碼如下:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
只不過最後一個引數是根據root是否為null來決定的,這也比較好理解,如果你沒有傳入root本身就沒有父view可繫結,所以attachToRoot自然是false
-
介紹View inflate(int, ViewGroup) 方法,第二個引數是否傳入null,所產生的不同的結果。
- Fragment中使用,在onCreateView中
@Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { LogUtils.d("-----"+container.toString()
如何正確使用呢? 兩種方案:
View inflate = inflater.inflate(R.layout.fragment, null); View inflate = inflater.inflate(R.layout.fragment, container,false); //根據原始碼可知,root傳入null和attachToRoot傳入false等價
- 其他常規用法基本原則是不變的,如下面程式碼:
FrameLayout viewById = findViewById(R.id.fl); View inflate = getLayoutInflater().inflate(R.layout.fragment,null); LogUtils.d("-----"+inflate.getParent().toString()); viewById.addView(inflate); //如上程式碼空指標錯誤,inflate.getParent()為null,常規填充是不會有父view的。
-
繼續介紹傳入不同的第三個引數,view會有不同的顯示效果,原始碼的中的關鍵程式碼:
/**
* @param root Optional view to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of LayoutParams values for root of the returned
*hierarchy (if <em>attachToRoot</em> is false.)
*/
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
//主要部分
if (TAG_MERGE.equals(name)) {
//如果root為null或者不繫結到root,則佈局效果都是按照父view的來的
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
// 這個傳入的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);
//初始化出來的子view不繫結到root上,則設定指定父佈局的引數(算是一組參考的佈局引數),後續需要自己呼叫addview方法,並且並不一定必須add到這個佈局上
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.
//如果繫結到root上的話,就直接通過addview來加入到root的佈局
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.
// 未指定父佈局或者不繫結的話直接返回解析好view
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription()
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
上面的註解也還算詳細了,如有問題望各位大佬指點。
如上基本完成分析,下面總結一下使用的注意事項。
- 在繫結view的時候要注意是inflate出來的view已經預設添加了父view
- 如果還不確定要新增到的view,直接傳入null即可,會減少一些計算邏輯
- 如果attachToRoot傳入true,則不可以在呼叫addview方法,將該view新增到其他view上