前言
AttributeSet與XmlPullParser這2個接口在解析xml文件時經(jīng)常配合使用提揍,二者可進(jìn)行相互轉(zhuǎn)換,理清二者的關(guān)系對于深入理解LayoutIflater.inflate()源碼時俩由,有著重要作用。
故本文的目的在于理清二者之間的關(guān)系癌蚁,簡述XmlPullParser的基本用法幻梯,為后續(xù)理解LayoutIflater.inflate方法源碼打下基礎(chǔ)。
類圖結(jié)構(gòu)
AttributeSet與XmlPullParser的類圖關(guān)系如下圖所示:
AttributeSet接口抽象了訪問xml節(jié)點屬性的方法努释,XmlPullPaser接口抽象了解析xml文件的方法碘梢,這個2個接口具有部分相同的方法聲明,如下圖所示洽洁。這些方法是XmlPull解析常用來訪問xml節(jié)點屬性的方法痘系。
int getAttributeCount();
String getAttributeName(int index);
String getAttributeValue(int index);
String getAttributeValue(String namespace, String name);
String getPositionDescription();
AttributeSet與XmlPullParser的關(guān)系
在Android中AttributeSet接口的實現(xiàn)類有XmlPullAttribute和Parser(XmlResourceParser接口的實現(xiàn)類,該接口繼承了XmlPullParser和AttributeSet接口)饿自。
我們先來看看XmlPullAttribute的實現(xiàn)汰翠,部分代碼如下:
class XmlPullAttributes implements AttributeSet {
public XmlPullAttributes(XmlPullParser parser) {
mParser = parser;
}
public int getAttributeCount() {
return mParser.getAttributeCount();
}
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
public String getAttributeValue(int index) {
return mParser.getAttributeValue(index);
}
public String getAttributeValue(String namespace, String name) {
return mParser.getAttributeValue(namespace, name);
}
public String getPositionDescription() {
return mParser.getPositionDescription();
}
public int getAttributeNameResource(int index) {
return 0;
}
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
return XmlUtils.convertValueToList(
getAttributeValue(namespace, attribute), options, defaultValue);
}
............
//AttributeSet接口的剩余方法實現(xiàn)省略,其實現(xiàn)與getAttributeListValue()方法
//相似昭雌,都依賴XmlUtils與getAttributeValue()复唤。
}
可見該類關(guān)聯(lián)了一個XmlPullParser類型的成員變量(實際上是KXmlParser),AttributeSet接口與XmlPullParser接口相同聲明函數(shù)的實現(xiàn)都是調(diào)用的mParser的同名函數(shù)烛卧;而AttributeSet接口的其他方法皆依賴于getAttributeValue方法來實現(xiàn)佛纫,即也依賴于mParser。
從代碼實現(xiàn)來看总放,很自然的會聯(lián)想到裝飾者設(shè)計模式呈宇,雖然從類圖結(jié)構(gòu)上來看,并非屬于嚴(yán)格意義上的裝飾者模式局雄,但我想可以將AttributeSet理解為XmlPullParser的裝飾者甥啄,只不過這個裝飾者沒有全部裝飾而已,只是裝飾了XmlParser用于訪問xml節(jié)點屬性的那一部分炬搭。
自創(chuàng)部分裝飾設(shè)計蜈漓,強行解說2333....
綜上所述:AttributeSet是對XmlPullParser的部分裝飾穆桂,裝飾了用于訪問xml節(jié)點屬性的部分∪谒洌或者理解為AttributeSet是對XmlPullParser中用于訪問xml節(jié)點屬性方法的封裝享完。
至于為什么要這么做呢?拙見如下:
- AttributeSet這個接口更適合Android的設(shè)計有额,其語意性更強般又;如果將View的構(gòu)造函數(shù)中的AttributeSet換為XmlPullParser,看起來是不是有一種別扭的感覺的谆吴,而AttributeSet(屬性集合)這個命名更易讓人理解接受倒源,裝飾的作用之一就是可以“改頭換面”嘛苛预。
- 單一職責(zé)原則句狼,XmlPullParser接口的職責(zé)不單單是用于訪問xml節(jié)點的屬性,從中分離出一個用于訪問節(jié)點屬性的接口豈不美哉热某?
- 對XmlPullParser中的Xml節(jié)點屬性訪問方法進(jìn)行擴展腻菇,方便使用,如getAttributeIntValue方法可直接獲取int類型的屬性值昔馋,若使用XmlPullParser則只能獲取String類型的數(shù)據(jù)筹吐,還需要我們自己轉(zhuǎn)換一次。
下面來看看XmlResourceParser接口秘遏。
該接口同時繼承了XmlPullParser接口以及AttributeSet接口丘薛,作用有二。
- 對XmlPullParser起到部分裝飾的作用邦危,實現(xiàn)XmlResourceParser接口洋侨,即可達(dá)到目的。
- 繼承XmlPullParser接口倦蚪,實現(xiàn)一個專用于Android Xml資源文件的解析器希坚,如解析layout、drawable等目錄下的xml文件陵且。
Android中的常見打開方式如下:
Resources resources = getResources();
XmlResourceParser parser0 = resources.getXml(R.xml.test);
XmlResourceParser parser1 = resources.getLayout(R.layout.activity_main);
XmlResourceParser parser2 = resources.getAnimation(R.anim.test);
Drawable目錄下的xml解析并未提供我們可直接獲取XmlResourceParser的API裁僧,其解析封裝在ResourseImpl類(@hide)的方法中。
/**
* Loads a drawable from XML or resources stream.
*/
private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,Resources.Theme theme) {
............
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXml(wrapper, rp, theme);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
is.close();
}
............
}
XmlResourceParser的實現(xiàn)類為Parser類慕购,該類屬于BlockXml類的一個內(nèi)部類聊疲,用于解析Android中的layout布局文件。
這里說一個小技巧沪悲,方便想要自行查看源碼的朋友:
在Android Studio中可通過雙擊Shift鍵來快速查找類文件获洲,可以查到一些隱藏類文件的源碼。
總結(jié):
1可训、AttributeSet接口是對XmlPullParser接口的部分裝飾昌妹,裝飾了XmlPullParser中xml節(jié)點屬性的訪問方法捶枢。AttributeSet接口語意性更強,更適合Android架構(gòu)的整體設(shè)計飞崖。
2烂叔、其實現(xiàn)類無論是Parser還是XmlPullAttribute,它只是起到一個裝飾作用固歪,最終對xml節(jié)點屬性的訪問都是通過里面的XmlPullParser的節(jié)點屬性訪問方法來完成的蒜鸡。
至此,AttributeSet與XmlPullParser的關(guān)系已經(jīng)分析清楚牢裳,后文只是方便自己后續(xù)查看逢防,主要記錄XmlPullParser的基本使用。
轉(zhuǎn)換分析
XmlPullParser轉(zhuǎn)換為AttributeSet的方法為:
Xml.asAttributeSet(parser0);
進(jìn)去看看源碼:
//如果傳入的XmlPullParser類型為XmlResourceParser(Parser)蒲讯,返回的就是XmlResourceParser(Parser)
//反之則new 一個XmlPullAttributeSet
public static AttributeSet asAttributeSet(XmlPullParser parser) {
return (parser instanceof AttributeSet)
? (AttributeSet) parser
: new XmlPullAttributes(parser);
}
XmlPullParser的基本使用
由上文可知忘朝,Android中XmlPull解析器有2種類型,一種是專用于Android xml資源文件的解析器判帮,一種是org.xmlpull.v1自帶的xml解析器局嘁,無論是那種類型,對外而言晦墙,其用法都一樣悦昵,這就是面向接口編程的一個好處。
實際應(yīng)用當(dāng)中晌畅,使用較多的應(yīng)該是org.xmlpull.v1自帶的xml解析器但指,畢竟Android的專用資源解析器,它已經(jīng)封裝好自動解析了抗楔。
public static void main (String args[])
throws XmlPullParserException, IOException
{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//區(qū)分namespace
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
/**
* 解析最開始并非位于START_DOCUMENT,多次調(diào)用next棋凳,會依次位于
* START_DOCUMENT
* START_TAG(XML節(jié)點屬性解析只能在該事件類型中進(jìn)行解析)
* TEXT(無Text部分“Hello World”則跳過該事件類型)
* END_TAG
* End document
*/
xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
System.out.println("Start tag "+xpp.getName());
} else if(eventType == XmlPullParser.END_TAG) {
System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
System.out.println("End document");
}
結(jié)果:
Start document
Start tag foo
Text Hello World!
End tag foo
End document