部落格地址:blog.csdn.net/l540675759/…
前言
如果讀者沒有閱讀過該系列部落格,建議先閱讀下博文說明,這樣會對後續的閱讀部落格思路上會有一個清晰的認識。
Android 中LayoutInflater(佈局載入器)系列博文說明
導航
Android 中LayoutInflater(佈局載入器)系列博文說明
Android 中LayoutInflater(佈局載入器)系列之介紹篇
Android 中LayoutInflater(佈局載入器)系列之原始碼篇
Android 中LayoutInflater(佈局載入器)原始碼篇之createViewFromTag方法
Android 中LayoutInflater(佈局載入器)原始碼篇之rInflate方法
Android 中LayoutInflater(佈局載入器)系列之實戰篇
概述
本篇部落格,是屬於Android 中LayoutInflater(佈局載入器)原始碼篇其中一個部分,專門介紹rInflate方法的流程,具體有以下幾部分:
-
一些不常見的標籤的解析方法以及使用,例如:requestFocus、tag
-
一個XML節點,變成一個View到底是怎麼做到的?
-
XML深度是什麼,有什麼作用?
rInflate()的原始碼分析
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
//獲取該標籤的深度
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
//如果該節點為requestFocus
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
//如果該節點為tag
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
//如果該節點為include標籤
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
//解析include標籤
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
//如果該節點為Merge
throw new InflateException("<merge /> must be the root element");
} else {
//否則屬於正常的View
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//接下來解析子View
rInflateChildren(parser, view, attrs, true);
//注意這裡直接進行addView操作
viewGroup.addView(view, params);
}
}
//如果解析完成,需要通知父View,解析完成。
if (finishInflate) {
parent.onFinishInflate();
}
}複製程式碼
在rInflate這裡做的操作,就是識別這些節點,然後對應解析形成響應的元素,下面我們會根據程式碼,一段一段分析rInflate都做了什麼.
(1)如果發現requestFocus標籤,則呼叫父View的requestFocus()方法。
requestFocus標籤使用:
<EditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- 當前控制元件處於焦點狀態 -->
<requestFocus />複製程式碼
parseRequestFocus方法:
private void parseRequestFocus(XmlPullParser parser, View view)
throws XmlPullParserException, IOException {
//呼叫其父View的requestFocus()方法
view.requestFocus();
consumeChildElements(parser);
}複製程式碼
(2)如果發現tag標籤,為其設定(key,value)模式的tag。
tag標籤使用:
<Button
android:id="@+id/tag_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="openClickNotification"
android:text="自定義帶監聽事件的通知">
<tag
android:id="@+id/tag_id"
android:value="@string/app_name" />
</Button>複製程式碼
parseViewTag方法 :
private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
throws XmlPullParserException, IOException {
final Context context = view.getContext();
final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
//這裡設定tag的key
final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
//這裡設定tag的value
final CharSequence value = ta.getText(R.styleable.ViewTag_value);
view.setTag(key, value);
ta.recycle();
consumeChildElements(parser);
}複製程式碼
在parseViewTag()方法中,會把(key,value)形式的tag賦予View。
Key指的是R.id.tag_id對應的int型別資料;
Value指的是R.string.app_name的String型別資料;
(3)如果是Include標籤,這裡開始先獲取了Include的深度
final int depth = parser.getDepth();複製程式碼
所謂深度就是XML的層級關係,例如這樣:
<!-- outside --> 0
<root> 1
sometext 1
<foobar> 2
</foobar> 2
</root> 1
<!-- outside --> 0複製程式碼
判斷該Include標籤的深度是否是0,如果為0,則丟擲異常,因為include不能為根元素。
然後根據Include的特性,對其layout屬性內的佈局進行解析,這段內容和解析內容重複較高,暫時先了解到這裡,後續可能單獨開一篇文章做分析。
(4)如果是Merge標籤,那麼會直接丟擲異常,因為Merge必須為根元素,也就是深度為0的節點。
(5)最後是其他標籤,例如View,一起其他的一些標籤
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);複製程式碼
在載入View的過程,大致分為三個階段:
-
createViewFromTag() 見名知意,根據節點名稱建立View
-
rInflateChildren() 載入該節點內子類
-
parent.addView() 最後將該View新增進Parent佈局
第一階段 : createViewFromTag()
createViewFromTag()是根據name(節點名稱)來解析出View的一個方法,這裡直分析rInflate()的流程,而createViewFromTag()會在另一部分介紹,連結如下:
Android 中LayoutInflater(佈局載入器)原始碼篇之createViewFromTag方法
第二階段 :rInflateChildren()
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}複製程式碼
這裡可以看到,這裡會將解析出來的View作為Root(父View),繼續進行子節點的解析,會繼續呼叫,直到無法解析。
這裡的無法解析是指:
-
當前解析的標識為XmlPullParser.END_TAG(節點結束的識別符號),並且深度不在父節點的標籤內。
-
或者type 為 XmlPullParser.END_DOCUMENT(文件結束的識別符號)。
第三階段 parent.addView()將View新增進父View中
viewGroup.addView(view, params);複製程式碼
這段話,不難理解,就是將解析出的View,新增到父View中。
流程圖
如果圖片比較大,請下載到本地,或者放大120%檢視。