怎么dom解析xml如下的xml

用Python解析XML的几种常见方法的介绍
转载 &更新时间:日 11:07:26 & 作者:taiyang1987912
这篇文章主要介绍了用Python解析XML的几种常见方法,包括快速的使用ElementTree模块等方法的实例介绍,需要的朋友可以参考下
&&&&&& XML(eXtensible Markup Language)指可扩展标记语言,被设计用来传输和存储数据,已经日趋成为当前许多新生技术的核心,在不同的领域都有着不同的应用。它是web发展到一定阶段的必然产物,既具有SGML的核心特征,又有着HTML的简单特性,还具有明确和结构良好等许多新的特性。
&&&&&&& python解析XML常见的有三种方法:一是xml.dom.*模块,它是W3C DOM API的实现,若需要处理DOM API则该模块很适合,注意xml.dom包里面有许多模块,须区分它们间的不同;二是xml.sax.*模块,它是SAX API的实现,这个模块牺牲了便捷性来换取速度和内存占用,SAX是一个基于事件的API,这就意味着它可以“在空中”处理庞大数量的的文档,不用完全加载进内存;三是xml.etree.ElementTree模块(简称 ET),它提供了轻量级的Python式的API,相对于DOM来说ET 快了很多,而且有很多令人愉悦的API可以使用,相对于SAX来说ET的ET.iterparse也提供了 “在空中” 的处理方式,没有必要加载整个文档到内存,ET的性能的平均值和SAX差不多,但是API的效率更高一点而且使用起来很方便。
&&&&& 解析的xml文件(country.xml):
在CODE上查看代码片派生到我的代码片
&?xml version="1.0"?&
&country name="Singapore"&
&rank&4&/rank&
&year&2011&/year&
&gdppc&59900&/gdppc&
&neighbor name="Malaysia" direction="N"/&
&/country&
&country name="Panama"&
&rank&68&/rank&
&year&2011&/year&
&gdppc&13600&/gdppc&
&neighbor name="Costa Rica" direction="W"/&
&neighbor name="Colombia" direction="E"/&
&/country&
1、xml.etree.ElementTree
&&&&&&& ElementTree生来就是为了处理XML,它在Python标准库中有两种实现:一种是纯Python实现的,如xml.etree.ElementTree,另一种是速度快一点的xml.etree.cElementTree。注意:尽量使用C语言实现的那种,因为它速度更快,而且消耗的内存更少。
在CODE上查看代码片派生到我的代码片
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
&&&&&&& 这是一个让Python不同的库使用相同API的一个比较常用的办法,而从Python 3.3开始ElementTree模块会自动寻找可用的C库来加快速度,所以只需要import xml.etree.ElementTree就可以了。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/evn python
#coding:utf-8
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
import sys
tree = ET.parse("country.xml")
#打开xml文档
#root = ET.fromstring(country_string) #从字符串传递xml
root = tree.getroot()
#获得root节点
except Exception, e:
print "Error:cannot parse file:country.xml."
sys.exit(1)
print root.tag, "---", root.attrib
for child in root:
print child.tag, "---", child.attrib
print "*"*10
print root[0][1].text
#通过下标访问
print root[0].tag, root[0].text
print "*"*10
for country in root.findall('country'): #找到root节点下的所有country节点
rank = country.find('rank').text
#子节点下节点rank的值
name = country.get('name')
#子节点下属性name的值
print name, rank
#修改xml文件
for country in root.findall('country'):
rank = int(country.find('rank').text)
if rank & 50:
root.remove(country)
tree.write('output.xml')
运行结果:
参考:https://docs.python.org/2/library/xml.etree.elementtree.html
2、xml.dom.*
&&&&&&& 文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。一个 DOM 的解析器在解析一个XML文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。python中用xml.dom.minidom来解析xml文件,例子如下:
在CODE上查看代码片派生到我的代码片
#!/usr/bin/python
#coding=utf-8
from xml.dom.minidom import parse
import xml.dom.minidom
# 使用minidom解析器打开XML文档
DOMTree = xml.dom.minidom.parse("country.xml")
Data = DOMTree.documentElement
if Data.hasAttribute("name"):
print "name element : %s" % Data.getAttribute("name")
# 在集合中获取所有国家
Countrys = Data.getElementsByTagName("country")
# 打印每个国家的详细信息
for Country in Countrys:
print "*****Country*****"
if Country.hasAttribute("name"):
print "name: %s" % Country.getAttribute("name")
rank = Country.getElementsByTagName('rank')[0]
print "rank: %s" % rank.childNodes[0].data
year = Country.getElementsByTagName('year')[0]
print "year: %s" % year.childNodes[0].data
gdppc = Country.getElementsByTagName('gdppc')[0]
print "gdppc: %s" % gdppc.childNodes[0].data
for neighbor in Country.getElementsByTagName("neighbor"):
print neighbor.tagName, ":", neighbor.getAttribute("name"), neighbor.getAttribute("direction")
运行结果:
参考:https://docs.python.org/2/library/xml.dom.html
3、xml.sax.*
&&&&&& SAX是一种基于事件驱动的API,利用SAX解析XML牵涉到两个部分:解析器和事件处理器。其中解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件;而事件处理器则负责对事件作出相应,对传递的XML数据进行处理。python中使用sax方式处理xml要先引入xml.sax中的parse函数,还有xml.sax.handler中的ContentHandler。常使用在如下的情况下:一、对大型文件进行处理;二、只需要文件的部分内容,或者只需从文件中得到特定信息;三、想建立自己的对象模型的时候。
ContentHandler类方法介绍
(1)characters(content)方法
调用时机:
从行开始,遇到标签之前,存在字符,content的值为这些字符串。
从一个标签,遇到下一个标签之前, 存在字符,content的值为这些字符串。
从一个标签,遇到行结束符之前,存在字符,content的值为这些字符串。
标签可以是开始标签,也可以是结束标签。
(2)startDocument()方法
文档启动的时候调用。
(3)endDocument()方法
解析器到达文档结尾时调用。
(4)startElement(name, attrs)方法
遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。
(5)endElement(name)方法
遇到XML结束标签时调用。
在CODE上查看代码片派生到我的代码片
#coding=utf-8
#!/usr/bin/python
import xml.sax
class CountryHandler(xml.sax.ContentHandler):
def __init__(self):
self.CurrentData = ""
self.rank = ""
self.year = ""
self.gdppc = ""
self.neighborname = ""
self.neighbordirection = ""
# 元素开始事件处理
def startElement(self, tag, attributes):
self.CurrentData = tag
if tag == "country":
print "*****Country*****"
name = attributes["name"]
print "name:", name
elif tag == "neighbor":
name = attributes["name"]
direction = attributes["direction"]
print name, "-&", direction
# 元素结束事件处理
def endElement(self, tag):
if self.CurrentData == "rank":
print "rank:", self.rank
elif self.CurrentData == "year":
print "year:", self.year
elif self.CurrentData == "gdppc":
print "gdppc:", self.gdppc
self.CurrentData = ""
# 内容事件处理
def characters(self, content):
if self.CurrentData == "rank":
self.rank = content
elif self.CurrentData == "year":
self.year = content
elif self.CurrentData == "gdppc":
self.gdppc = content
if __name__ == "__main__":
# 创建一个 XMLReader
parser = xml.sax.make_parser()
# turn off namepsaces
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# 重写 ContextHandler
Handler = CountryHandler()
parser.setContentHandler(Handler)
parser.parse("country.xml")
运行结果:
4、libxml2和lxml解析xml
&&&&&& libxml2是使用C语言开发的xml解析器,是一个基于MIT License的免费开源软件,多种编程语言都有基于它的实现,python中的libxml2模块有点小不足的是:xpathEval()接口不支持类似模板的用法,但不影响使用,因libxml2采用C语言开发的,因此在使用API接口的方式上难免会有点不适应。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/python
#coding=utf-8
import libxml2
doc = libxml2.parseFile("country.xml")
for book in doc.xpathEval('//country'):
if book.content != "":
print "----------------------"
print book.content
for node in doc.xpathEval("//country/neighbor[@name = 'Colombia']"):
print node.name, (node.properties.name, node.properties.content)
doc.freeDoc()
&&&&&&& lxml是以libxml2为基础采用python语言开发的,从使用层面上说比lxml更适合python开发者,且xpath()接口支持类似模板的用法。
在CODE上查看代码片派生到我的代码片
#!/usr/bin/python
#coding=utf-8
import lxml.etree
doc = lxml.etree.parse("country.xml")
for node in doc.xpath("//country/neighbor[@name = $name]", name = "Colombia"):
print node.tag, node.items()
for node in doc.xpath("//country[@name = $name]", name = "Singapore"):
print node.tag, node.items()
(1)Python中XML解析可用的类库或模块有xml、libxml2 、lxml 、xpath等,需要深入了解的还需参考相应的文档。
(2)每一种解析方式都有自己的优点和缺点,选择前可以综合各个方面的性能考虑。
(3)若有不足,请留言,在此先感谢!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具I've been reading some tutorials on XmlPullParser in Android on how to parse XML data.
To be more specific, I'm using the XML from https://gdata.youtube.com/feeds/api/standardfeeds/top_rated
Here I simplify part on an entry from this feed (I hope without altering the structure) in:
&id&http://gdata.youtube.com/feeds/api/videos/abc45678qwe&/id&
&title type='text'&THE TITLE&/title&
&link rel='alternate' type='text/html' href='https://www.youtube.com/watch?v=abc45678qwe&feature=youtube_gdata'/&
&media:group&
&media:title type='plain'&THE TITLE&/media:title&
&yt:duration seconds='300'/&
&yt:videoid&abc45678qwe&/yt:videoid&
&/media:group&
&gd:rating average='1' max='5' min='1' numRaters='1' rel='http://schemas.google.com/g/2005#overall'/&
&yt:statistics favoriteCount='0' viewCount=''/&
&yt:rating numDislikes='111' numLikes='111'/&
I successfully get the title and the link with:
private String[] readEntry(XmlPullParser parser)
throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, null, "entry");
String title =
String link =
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
String name = parser.getName();
String rel = parser.getAttributeValue(null, "rel");
if (name.equalsIgnoreCase("title")) {
title = readTitle(parser);
} else if (name.equalsIgnoreCase("link")
&& rel.equals("alternate")) {
link = readLink(parser);
skip(parser);
return new String[] { title, link };
private String readLink(XmlPullParser parser)
throws XmlPullParserException, IOException {
String link = "";
parser.require(XmlPullParser.START_TAG, null, "link");
link = parser.getAttributeValue(null, "href");
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "link");
private String readTitle(XmlPullParser parser)
throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, null, "title");
String title = readText(parser);
parser.require(XmlPullParser.END_TAG, null, "title");
But no matter what I try, I'm not able to get the duration in seconds from &yt:duration seconds='300'/&.
Clearly it can't be accessed with something similar to the above methods, as handling namespaces should be required, but I'm not sure. Since I'm kinda lost on this, any suggestion is much appreciated. Thanks.
edit: I'm adding what I tried to enter the tag yt:duration.
I added other checks before skip(parser);. I.e.:
} else if (name.equalsIgnoreCase("yt:")) {
Utils.logger("i", "entering yt:", TAG);
readDuration(parser)
and I changed "yt:" with "yt", or "yt:duration with no result.
String namespace = parser.getNamespace();
and changing name.equalsIgnoreCase... with namespace.equalsIgnoreCase... I don't get the log entry, so I don't even had a way to try this:
private String readDuration(XmlPullParser parser)
throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, "yt", "duration");
String seconds = parser.getAttributeValue(null, "seconds");
parser.nextTag();
parser.require(XmlPullParser.END_TAG, "yt", "duration");
Utils.logger("i", "duration: " + seconds + " seconds", TAG);
Addition made "on request". I'm not sure it's useful enough.
解决方案 XmlPullParser seems to have the ability to be namespace aware, the difference is it has to be explicitly set. Per the documentation of :
Specifies that the parser produced by this factory will provide
support for XML namespaces. By default the value of this is set to
You might want to try that option.
Also, as mentioned in the comments I have tried to traverse through your xml with DOM with zero issues, below is the source code of printing all the duration values (just to let you know, this is to be run as a Java program and not within the ADT):
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException {
InputStream path = new URL(
"https://gdata.youtube.com/feeds/api/standardfeeds/top_rated")
.openStream();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(path);
traverse(document.getDocumentElement());
public static void traverse(Node node) {
NodeList list = node.getChildNodes();
for (int i = 0; i & list.getLength(); i++) {
Node currentNode = list.item(i);
traverse(currentNode);
if (node.getNodeName().equals("yt:duration")) {
Element durationElement = (Element)
System.out.println(durationElement.getAttribute("seconds"));
Output I get:
I always prefer recursion (as above) with DOM as it simplifies the full traversal thereby providing the flexibility too.
If you want to know more about grouping these elements together, you can refer to my post
本文地址: &
我一直在阅读关于 XmlPullParser 一些教程在Android中如何解析XML数据。更具体地讲,我使用的XML从 https://gdata.youtube.com/feeds/api/standardfeeds/top_rated
下面我简化从这个进给(我希望在不改变结构)中的一个条目的部分: <输入><&ID GT; HTTP://gdata.youtube.com/feeds/api/videos/abc45678qwe& / ID>[...]<标题类型='文本'&在TITLE< /标题>[...]<链接相对='备用'类型='text / html的“HREF =的”https://www.youtube.com/watch V = abc45678qwe&放大器;放大器;特征= youtube_gdata?'/>[...]<介质:组&[...]<媒体:标题类型='纯'&在TITLE< /媒体:标题>< YT:持续时间秒='300'/>[...]< YT:视频ID> abc45678qwe< / YT:视频ID>< /媒体:组&< GD:评分平均='1'最大值=“5”分钟='1'numRaters ='1'的rel =“HTTP://schemas.google.com/g/2005#overall'/&< YT:统计favoriteCount ='0'观看次数=“”/>< YT:评价numDislikes ='111'numLikes =“111”/>< /进入> 我顺利地拿到冠军,并与链接: 私有String [] readEntry(XmlPullParser解析器)
抛出XmlPullParserException,IOException异常{
parser.require(XmlPullParser.START_TAG,空,“项”);
字符串title = NULL;
字符串link = NULL;
而(parser.next()!= XmlPullParser.END_TAG){
如果(parser.getEventType()!= XmlPullParser.START_TAG){
字符串名称= parser.getName();
字符串的rel = parser.getAttributeValue(NULL,“相对”);
如果(name.equalsIgnoreCase(“标题”)){
标题= readTitle(分析器);
}否则如果(name.equalsIgnoreCase(“链接”)
&功放;&安培; rel.equals(“备用”)){
链接=的readlink(分析器);
跳过(分析器);
返回新的String [] {标题,链接};}私人字符串的readlink(XmlPullParser解析器)
抛出XmlPullParserException,IOException异常{
字符串link =“”;
parser.require(XmlPullParser.START_TAG,空,“链接”);
链接= parser.getAttributeValue(NULL,“HREF”);
parser.nextTag();
parser.require(XmlPullParser.END_TAG,空,“链接”);
返回的链接;}私人字符串readTitle(XmlPullParser解析器)
抛出XmlPullParserException,IOException异常{
parser.require(XmlPullParser.START_TAG,空,“称号”);
字符串title = READTEXT(分析器);
parser.require(XmlPullParser.END_TAG,空,“称号”);
返回称号;} 但无论我怎么努力,我无法从获得秒持续时间< YT:持续时间秒='300'/>
显然不能用类似上述方法访问的东西,作为处理命名空间应必需的,但我不知道。因为我有点失去了这一点,任何的建议是pciated多少AP $ P $。谢谢你。 ==== 编辑:我添加什么,我试图进入标记 YT:病程 我以前添加其他检查跳过(分析器); 。即: }否则如果(name.equalsIgnoreCase(“YT”)){
Utils.logger(“I”,“进入YT”,TAG);
readDuration(分析器)} 和我换“YT”与“YT”或“YT :病程没有结果结果另外随着 字符串的命名空间= parser.getNamespace(); 和不断变化的 name.equalsIgnoreCase ... 与 namespace.equalsIgnoreCase ... 我没有得到日志条目,所以我甚至不有办法试试这个: 私人字符串readDuration(XmlPullParser解析器)
抛出XmlPullParserException,IOException异常{
parser.require(XmlPullParser.START_TAG“YT”,“时间”);
串秒= parser.getAttributeValue(NULL,“秒”);
parser.nextTag();
parser.require(XmlPullParser.END_TAG“YT”,“时间”);
Utils.logger(“I”,“持续的时间:”+秒+“秒”,TAG);
返回秒;} 增加作出了“关于要求”。我不知道这是不够用。解决方案
XmlPullParser 似乎已是名称空间感知的能力,不同的是它必须被明确地设置。每
XmlPullParseFactory#setNamespaceAware
指定由该工厂生产的解析器将提供
支持XML名称空间。默认的这个值被设置到
假的。您可能会想尝试该选项。此外,如我曾尝试通过你的XML与DOM零问题遍历评论中提到,下面是打印所有时间价值的源泉code(只是让你知道,这是要运行作为的Java 程序,而不是在 ADT ) 公共静态无效的主要(字串[] args)抛出的ParserConfigurationException,
的SAXException,IOException异常{
InputStream的路径=新的URL(
“https://gdata.youtube.com/feeds/api/standardfeeds/top_rated”)
.openStream();
工厂的DocumentBuilderFactory = DocumentBuilderFactory.newInstance();
的DocumentBuilder建设者= factory.newDocumentBuilder();
文献文件= builder.parse(路径);
遍历(document.getDocumentElement());
公共静态无效的移动(节点node){
节点列表清单= node.getChildNodes();
的for(int i = 0; I< list.getLength();我++){
节点currentNode = list.item(ⅰ);
遍历(currentNode);
如果(node.getNodeName()等于(“YT:持续时间”)){
元素durationElement =(元素)节点;
的System.out.println(durationElement.getAttribute(“秒”));
} 输出我得到:
5636122526521922025926737620512730824917162220183298172267204209 我总是preFER递归(如上)与 DOM ,因为它简化了充分遍历从而提供灵活了。如果您想了解更多有关这些元素组合在一起,你可以参考我的文章here为好。
本文地址: &
扫一扫关注IT屋
微信公众号搜索 “ IT屋 ” ,选择关注
与百万开发者在一起
(window.slotbydup = window.slotbydup || []).push({
id: '5828425',
container: s,
size: '300,250',
display: 'inlay-fix'Android中xml布局文件解析流程浅析(上)
绘制流程的三个步骤,即:
& 1、 &measure过程 ---
& 2、 layout 过程 &
& --- 布局过程
& & 3、 draw 过程 &
& &--- 绘制过程
& & & 要想对Android
中View这块深入理解,对这三个步骤地学习是必不可少的 。
& & 今天,我着重讲解下如下三个内容:
1、&measure过程
& & 2、WRAP_CONTENT、MATCH_PARENT/FILL_PARENT属性的原理说明
& & 3、xml布局文件解析成View树的流程分析。
&希望对大家能有帮助。-
-&&分析版本基于Android
&1、WRAP_CONTENT、MATCH_PARENT/FILL_PARENT&
&初入Android殿堂的同学们,对这三个属性一定又爱又恨。爱的是使用起来挺爽地---照葫芦画瓢即可,恨的
却是时常混淆这几个属性地意义,需要三思而后行。在带着大家重温下这几个属性的用法吧(希望我没有啰嗦)。
这三个属性都用来适应视图的水平或垂直大小,一个以视图的内容或尺寸为基础的布局比精确地指定视图范围
更加方便。
& & & ①
&fill_parent
& 设置一个视图的布局为fill_parent将强制性地使视图扩展至父元素大小。
& & ② match_parent
中match_parent和fill_parent意思一样,但match_parent更贴切,于是从2.2开始两个词都可以
用,但2.3版本后建议使用match_parent。
& & &③
wrap_content
自适应大小,强制性地使视图扩展以便显示其全部内容。以TextView和ImageView控件为例,设置为
&wrap_content将完整显示其内部的文本和图像。布局元素将根据内容更改大小。
可不要重复造轮子,以上摘自&&&&。
当然,我们可以设置View的确切宽高,而不是由以上属性指定。
android:layout_weight="wrap_content"&&&//自适应大小
android:layout_weight="match_parent"&&&//与父视图等高
android:layout_weight="fill_parent"&&&&//与父视图等高
android:layout_weight="100dip"&&&&&&&&&//精确设置高度值为&100dip&&
android:layout_weight="wrap_content"
//自适应大小
android:layout_weight="match_parent"
//与父视图等高
android:layout_weight="fill_parent"
//与父视图等高
android:layout_weight="100dip"
//精确设置高度值为 100dip
接下来,我们需要转换下视角,看看ViewGroup.LayoutParams类及其派生类。
&2、ViewGroup.LayoutParams类及其派生类
&ViewGroup.LayoutParams类说明
& & Android
API中如下介绍:
LayoutParams are used by views to tell their parents how they want
to be laid out.
&意思大概是说:&View通过LayoutParams类告诉其父视图它想要地大小(即,长度和宽度)。
因此,每个View都包含一个ViewGroup.LayoutParams类或者其派生类,View类依赖于ViewGroup.LayoutParams。
路径:frameworks\base\core\java\android\view\View.java
public&class&View&implements&Drawable.Callback,&KeyEvent.Callback,&AccessibilityEventSource&{&&
&&//该View拥有的&LayoutParams属性,父试图添加该View时,会为其赋值,特别注意,其类型为ViewGroup.LayoutParams。
&&protected&ViewGroup.LayoutParams&mLayoutP&&&&
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
//该View拥有的 LayoutParams属性,父试图添加该View时,会为其赋值,特别注意,其类型为ViewGroup.LayoutParams。
protected ViewGroup.LayoutParams mLayoutP
&ViewGroup.LayoutParams源码分析
路径位于:frameworks\base\core\java\android\view\ViewGroup.java
public&abstract&class&ViewGroup&extends&View&implements&ViewParent,&ViewManager&{&&
&&&&&public&static&class&LayoutParams&{&&
&&&&&&&&&&
&&&&&&&&@Deprecated&&
&&&&&&&&public&static&final&int&FILL_PARENT&=&-1;&&//&注意值为-1,Android2.2版本不建议使用
&&&&&&&&&&
&&&&&&&&public&static&final&int&MATCH_PARENT&=&-1;&//&注意值为-1
&&&&&&&&&&
&&&&&&&&public&static&final&int&WRAP_CONTENT&=&-2;&//&注意值为-2
&&&&&&&&&&
&&&&&&&&public&int&&&//该View的宽度,可以为WRAP_CONTENT/MATCH_PARENT&或者一个具体值
&&&&&&&&&&
&&&&&&&&public&int&&//该View的高度,可以为WRAP_CONTENT/MATCH_PARENT&或者一个具体值
&&&&&&&&&&
&&&&&&&&public&LayoutAnimationController.AnimationParameters&layoutAnimationParameters;&&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(Context&c,&AttributeSet&attrs)&{&&
&&&&&&&&&&&&TypedArray&a&=&c.obtainStyledAttributes(attrs,&R.styleable.ViewGroup_Layout);&&
&&&&&&&&&&&&setBaseAttributes(a,&&
&&&&&&&&&&&&&&&&&&&&R.styleable.ViewGroup_Layout_layout_width,&&
&&&&&&&&&&&&&&&&&&&&R.styleable.ViewGroup_Layout_layout_height);&&
&&&&&&&&&&&&a.recycle();&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(int&width,&int&height)&{&&
&&&&&&&&&&&&this.width&=&&&
&&&&&&&&&&&&this.height&=&&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(LayoutParams&source)&{&&
&&&&&&&&&&&&this.width&=&source.&&
&&&&&&&&&&&&this.height&=&source.&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&LayoutParams()&{&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&protected&void&setBaseAttributes(TypedArray&a,&int&widthAttr,&int&heightAttr)&{&&
&&&&&&&&&&&&width&=&a.getLayoutDimension(widthAttr,&"layout_width");&&
&&&&&&&&&&&&height&=&a.getLayoutDimension(heightAttr,&"layout_height");&&
&&&&&&&&}&&
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
public static class LayoutParams {
@Deprecated
public static final int FILL_PARENT = -1;
// 注意值为-1,Android2.2版本不建议使用
public static final int MATCH_PARENT = -1; // 注意值为-1
public static final int WRAP_CONTENT = -2; // 注意值为-2
//该View的宽度,可以为WRAP_CONTENT/MATCH_PARENT 或者一个具体值
//该View的高度,可以为WRAP_CONTENT/MATCH_PARENT 或者一个具体值
public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
public LayoutParams(Context c, AttributeSet attrs) {
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
setBaseAttributes(a,
R.styleable.ViewGroup_Layout_layout_width,
R.styleable.ViewGroup_Layout_layout_height);
a.recycle();
public LayoutParams(int width, int height) {
this.width =
this.height =
public LayoutParams(LayoutParams source) {
this.width = source.
this.height = source.
LayoutParams() {
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
width = a.getLayoutDimension(widthAttr, "layout_width");
height = a.getLayoutDimension(heightAttr, "layout_height");
&我们发现FILL_PARENT/MATCH_PARENT值为 -1
,WRAP_CONETENT值为-2,是不是有点诧异? 将值
设置为负值的目的是为了区别View的具体值(an
exact size) 总是大于0的。
&ViewGroup子类可以实现自定义LayoutParams,自定义LayoutParams提供了更好地扩展性,例如LinearLayout
&就有LinearLayout.&LayoutParams自定义类(见下文)。整个LayoutParams类家族还是挺复杂的。
ViewGroup.LayoutParams及其常用派生类的类图(部分类图)如下:
& & 该类图是在太庞大了,大家有兴趣的去看看Android
前面我们说过,每个View都包含一个ViewGroup.LayoutParams类或者其派生类,下面我们的疑问是Android框架
&中时如何为View设置其LayoutParams属性的。
& &有两种方法会设置View的LayoutParams属性:
直接添加子View时,常见于如下几种方法:ViewGroup.java
//Adds&a&child&view.&&&&
void&addView(View&child,&int&index)&&
//Adds&a&child&view&with&this&ViewGroup's&default&layout&parameters&
//and&the&specified&width&and&height.
void&addView(View&child,&int&width,&int&height)&&
//Adds&a&child&view&with&the&specified&layout&parameters.&&&&&&&
void&addView(View&child,&ViewGroup.LayoutParams&params)&&
//Adds a child view.
void addView(View child, int index)
//Adds a child view with this ViewGroup's default layout parameters
//and the specified width and height.
void addView(View child, int width, int height)
//Adds a child view with the specified layout parameters.
void addView(View child, ViewGroup.LayoutParams params)
&三个重载方法的区别只是添加View时构造LayoutParams对象的方式不同而已,稍后我们探寻一下它们的源码。
通过xml布局文件指定某个View的属性为:android:layout_heigth=””以及android:layout_weight=””
总的来说,这两种方式都会设定View的LayoutParams属性值----指定的或者Default值。
方式1流程分析:
&直接添加子View时,比较容易理解,我们先来看看这种方式设置LayoutParams的过程:
&路径:\frameworks\base\core\java\android\view\ViewGroup.java
public&abstract&class&ViewGroup&extends&View&implements&ViewParent,&ViewManager&{&&
&&&&public&void&addView(View&child)&{&&
&&&&&&&&addView(child,&-1);&&
&&&&public&void&addView(View&child,&int&index)&{&&
&&&&&&&&LayoutParams&params&=&child.getLayoutParams();&&
&&&&&&&&if&(params&==&null)&{&&
&&&&&&&&&&&&params&=&generateDefaultLayoutParams();&//返回默认地LayoutParams类,作为该View的属性值
&&&&&&&&&&&&if&(params&==&null)&{//如果不能获取到LayoutParams对象,则抛出异常。
&&&&&&&&&&&&&&&&throw&new&IllegalArgumentException("generateDefaultLayoutParams()&cannot&return&null");&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&addView(child,&index,&params);&&
&&&&public&void&addView(View&child,&int&width,&int&height)&{&&
&&&&&&&&//返回默认地LayoutParams类,作为该View的属性值
&&&&&&&&final&LayoutParams&params&=&generateDefaultLayoutParams();&&&
&&&&&&&&params.width&=&&&&//重新设置width值
&&&&&&&&params.height&=&&//重新设置height值
&&&&&&&&addView(child,&-1,&params);&//这儿,我们有指定width、height的大小了。
&&&&public&void&addView(View&child,&LayoutParams&params)&{&&
&&&&&&&&addView(child,&-1,&params);&&
&&&&public&void&addView(View&child,&int&index,&LayoutParams&params)&{&&
&&&&&&&&...&&
&&&&&&&&//&addViewInner()&will&call&child.requestLayout()&when&setting&the&new&LayoutParams
&&&&&&&&//&therefore,&we&call&requestLayout()&on&ourselves&before,&so&that&the&child's&request
&&&&&&&&//&will&be&blocked&at&our&level
&&&&&&&&requestLayout();&&
&&&&&&&&invalidate();&&
&&&&&&&&addViewInner(child,&index,&params,&false);&&
&&&&protected&LayoutParams&generateDefaultLayoutParams()&{&&
&&&&&&&&//width&为&WRAP_CONTENT大小&,&height&为WRAP_CONTENT&
&&&&&&&&//ViewGroup的子类可以重写该方法,达到其特定要求。稍后会以LinearLayout类为例说明。
&&&&&&&&return&new&LayoutParams(LayoutParams.WRAP_CONTENT,&LayoutParams.WRAP_CONTENT);&&
&&&&private&void&addViewInner(View&child,&int&index,&LayoutParams&params,&&
&&&&&&&&&&&&boolean&preventRequestLayout)&{&&
&&&&&&&&if&(!checkLayoutParams(params))&{&//params对象是否为null
&&&&&&&&&&&&params&=&generateLayoutParams(params);&//如果params对象是为null,重新构造个LayoutParams对象
&&&&&&&&}&&
&&&&&&&&//preventRequestLayout值为false
&&&&&&&&if&(preventRequestLayout)&{&&&&
&&&&&&&&&&&&child.mLayoutParams&=&&//为View的mLayoutParams属性赋值
&&&&&&&&}&else&{&&
&&&&&&&&&&&&child.setLayoutParams(params);//为View的mLayoutParams属性赋值,但会调用requestLayout()请求重新布局
&&&&&&&&}&&
&&&&&&&&//if&else&语句会设置View为mLayoutParams属性赋值
&&&&&&&&...&&
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
public void addView(View child) {
addView(child, -1);
public void addView(View child, int index) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams(); //返回默认地LayoutParams类,作为该View的属性值
if (params == null) {//如果不能获取到LayoutParams对象,则抛出异常。
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
addView(child, index, params);
public void addView(View child, int width, int height) {
//返回默认地LayoutParams类,作为该View的属性值
final LayoutParams params = generateDefaultLayoutParams();
params.width =
//重新设置width值
params.height = //重新设置height值
addView(child, -1, params); //这儿,我们有指定width、height的大小了。
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
public void addView(View child, int index, LayoutParams params) {
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate();
addViewInner(child, index, params, false);
protected LayoutParams generateDefaultLayoutParams() {
//width 为 WRAP_CONTENT大小 , height 为WRAP_CONTENT
//ViewGroup的子类可以重写该方法,达到其特定要求。稍后会以LinearLayout类为例说明。
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
if (!checkLayoutParams(params)) { //params对象是否为null
params = generateLayoutParams(params); //如果params对象是为null,重新构造个LayoutParams对象
//preventRequestLayout值为false
if (preventRequestLayout) {
child.mLayoutParams = //为View的mLayoutParams属性赋值
child.setLayoutParams(params);//为View的mLayoutParams属性赋值,但会调用requestLayout()请求重新布局
//if else 语句会设置View为mLayoutParams属性赋值
主要功能就是在添加子View时为其构建了一个LayoutParams对象。但更重要的是,ViewGroup的子类可以重载
&上面的几个方法,返回特定的LayoutParams对象,例如:对于LinearLayout而言,则是LinearLayout.LayoutParams
&对象。这么做地目的是,能在其他需要它的地方,可以将其强制转换成LinearLayout.LayoutParams对象。
& &&LinearLayout重写函数地实现为:
public&class&LinearLayout&extends&ViewGroup&{&&
&&&&@Override&&
&&&&public&LayoutParams&generateLayoutParams(AttributeSet&attrs)&{&&
&&&&&&&&return&new&LinearLayout.LayoutParams(getContext(),&attrs);&&
&&&&@Override&&
&&&&protected&LayoutParams&generateDefaultLayoutParams()&{&&
&&&&&&&&//该LinearLayout是水平方向还是垂直方向
&&&&&&&&if&(mOrientation&==&HORIZONTAL)&{&&&
&&&&&&&&&&&&return&new&LayoutParams(LayoutParams.WRAP_CONTENT,&LayoutParams.WRAP_CONTENT);&&
&&&&&&&&}&else&if&(mOrientation&==&VERTICAL)&{&&
&&&&&&&&&&&&return&new&LayoutParams(LayoutParams.MATCH_PARENT,&LayoutParams.WRAP_CONTENT);&&
&&&&&&&&}&&
&&&&&&&&return&null;&&
&&&&@Override&&
&&&&protected&LayoutParams&generateLayoutParams(ViewGroup.LayoutParams&p)&{&&
&&&&&&&&return&new&LayoutParams(p);&&
&&&&&//自定义的LayoutParams类
&&&&public&static&class&LayoutParams&extends&ViewGroup.MarginLayoutParams&{&&
&&&&&&&&&&
&&&&&&&&@ViewDebug.ExportedProperty(category&=&"layout")&&
&&&&&&&&public&float&&&&&&&//&&见于属性,android:layout_weight=""&&;
&&&&&&&&&&
&&&&&&&&public&int&gravity&=&-1;&&//&见于属性,&android:layout_gravity=""&&;&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(Context&c,&AttributeSet&attrs)&{&&
&&&&&&&&&&&&super(c,&attrs);&&
&&&&&&&&&&&&TypedArray&a&=c.obtainStyledAttributes(attrs,&com.android.internal.R.styleable.LinearLayout_Layout);&&
&&&&&&&&&&&&weight&=&a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight,&0);&&
&&&&&&&&&&&&gravity&=&a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity,&-1);&&
&&&&&&&&&&&&a.recycle();&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(int&width,&int&height)&{&&
&&&&&&&&&&&&super(width,&height);&&
&&&&&&&&&&&&weight&=&0;&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&public&LayoutParams(int&width,&int&height,&float&weight)&{&&
&&&&&&&&&&&&super(width,&height);&&
&&&&&&&&&&&&this.weight&=&&&
&&&&&&&&}&&
&&&&&&&&public&LayoutParams(ViewGroup.LayoutParams&p)&{&&
&&&&&&&&&&&&super(p);&&
&&&&&&&&}&&
&&&&&&&&public&LayoutParams(MarginLayoutParams&source)&{&&
&&&&&&&&&&&&super(source);&&
&&&&&&&&}&&
public class LinearLayout extends ViewGroup {
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LinearLayout.LayoutParams(getContext(), attrs);
protected LayoutParams generateDefaultLayoutParams() {
//该LinearLayout是水平方向还是垂直方向
if (mOrientation == HORIZONTAL) {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
} else if (mOrientation == VERTICAL) {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
//自定义的LayoutParams类
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
@ViewDebug.ExportedProperty(category = "layout")
见于属性,android:layout_weight=""
public int gravity = -1;
// 见于属性, android:layout_gravity=""
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a =c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
a.recycle();
public LayoutParams(int width, int height) {
super(width, height);
weight = 0;
public LayoutParams(int width, int height, float weight) {
super(width, height);
this.weight =
public LayoutParams(ViewGroup.LayoutParams p) {
public LayoutParams(MarginLayoutParams source) {
super(source);
&LinearLayout.LayoutParams类继承至ViewGroup.MarginLayoutParams类,添加了对android:layout_weight以及
&android:layout_gravity这两个属性的获取和保存。而且它的重写函数返回的都是LinearLayout.LayoutParams
&类型。这样,我们可以再对子View进行其他操作时,可以将将其强制转换成LinearLayout.LayoutParams对象进行
&例如,LinearLayout进行measure过程,使用了LinearLayout.LayoutParam对象,有如下代码:
public&class&LinearLayout&extends&ViewGroup&{&&
&&&&@Override&&//onMeasure方法。
&&&&protected&void&onMeasure(int&widthMeasureSpec,&int&heightMeasureSpec)&{&&
&&&&&&&&//判断是垂直方向还是水平方向,这儿我们假设是VERTICAL垂直方向,
&&&&&&&&if&(mOrientation&==&VERTICAL)&{&&
&&&&&&&&&&&&measureVertical(widthMeasureSpec,&heightMeasureSpec);&&
&&&&&&&&}&else&{&&
&&&&&&&&&&&&measureHorizontal(widthMeasureSpec,&heightMeasureSpec);&&
&&&&&&&&}&&
&&&&&&void&measureVertical(int&widthMeasureSpec,&int&heightMeasureSpec)&{&&
&&&&&&&&&&&&mTotalLength&=&0;&&
&&&&&&&&&&&&...&&
&&&&&&&&&&&&//&See&how&tall&everyone&is.&Also&remember&max&width.
&&&&&&&&&&&&for&(int&i&=&0;&i&&&&++i)&{&&
&&&&&&&&&&&&&&&&final&View&child&=&getVirtualChildAt(i);&//获得索引处为i的子VIew&&&
&&&&&&&&&&&&&&&&...&&
&&&&&&&&&&&&&&&&//注意,我们将类型为&ViewGroup.LayoutParams的实例对象强制转换为了LinearLayout.LayoutParams,
&&&&&&&&&&&&&&&&//即父对象转换为了子对象,能这样做的原因就是LinearLayout的所有子View的LayoutParams类型都为
&&&&&&&&&&&&&&&&//LinearLayout.LayoutParams
&&&&&&&&&&&&&&&&LinearLayout.LayoutParams&lp&=&(LinearLayout.LayoutParams)&child.getLayoutParams();&&
&&&&&&&&&&&&&&&&...&&
&&&&&&&&}&&
public class LinearLayout extends ViewGroup {
//onMeasure方法。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//判断是垂直方向还是水平方向,这儿我们假设是VERTICAL垂直方向,
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0;
// See how tall everyone is. Also remember max width.
for (int i = 0; i & ++i) {
final View child = getVirtualChildAt(i); //获得索引处为i的子VIew
//注意,我们将类型为 ViewGroup.LayoutParams的实例对象强制转换为了LinearLayout.LayoutParams,
//即父对象转换为了子对象,能这样做的原因就是LinearLayout的所有子View的LayoutParams类型都为
//LinearLayout.LayoutParams
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
超类ViewGroup.LayoutParams强制转换为了子类LinearLayout.LayoutParams,因为LinearLayout的每个
”直接“子View的LayoutParams属性都是LinearLayout.LayoutParams类型,因此可以安全转换。
&PS : Android
2.3源码Launcher2中也实现了自定义的LayoutParams类,在IDLE界面的每个View至少包含如下
信息:所在X方向的单元格索引和高度、所在Y方向的单元格索引和高度等。
&&路径:&packages\apps\Launcher2\src\com\android\launcher2\CellLayout.java
public&class&CellLayout&extends&ViewGroup&{&&
&&&&...&&&
&&&&public&static&class&LayoutParams&extends&ViewGroup.MarginLayoutParams&{&&
&&&&&&&&&&&&&&
&&&&&&&&&&&&public&int&cellX;&&&//X方向的单元格索引
&&&&&&&&&&&&&&
&&&&&&&&&&&&public&int&cellY;&&&//Y方向的单元格索引
&&&&&&&&&&&&&&
&&&&&&&&&&&&public&int&cellHS&&//水平方向所占高度
&&&&&&&&&&&&&&
&&&&&&&&&&&&public&int&cellVS&&//垂直方向所占高度
&&&&&&&&&&&&...&&
&&&&&&&&&&&&public&LayoutParams(Context&c,&AttributeSet&attrs)&{&&
&&&&&&&&&&&&&&&&super(c,&attrs);&&
&&&&&&&&&&&&&&&&cellHSpan&=&1;&&//默认为高度&1
&&&&&&&&&&&&&&&&cellVSpan&=&1;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&public&LayoutParams(ViewGroup.LayoutParams&source)&{&&
&&&&&&&&&&&&&&&&super(source);&//默认为高度&1
&&&&&&&&&&&&&&&&cellHSpan&=&1;&&
&&&&&&&&&&&&&&&&cellVSpan&=&1;&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&
&&&&&&&&&&&&public&LayoutParams(int&cellX,&int&cellY,&int&cellHSpan,&int&cellVSpan)&{&&
&&&&&&&&&&&&&&&&super(LayoutParams.MATCH_PARENT,&LayoutParams.MATCH_PARENT);&&
&&&&&&&&&&&&&&&&this.cellX&=&cellX;&&
&&&&&&&&&&&&&&&&this.cellY&=&cellY;&&
&&&&&&&&&&&&&&&&this.cellHSpan&=&cellHS&&
&&&&&&&&&&&&&&&&this.cellVSpan&=&cellVS&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&...&&
&&&&&&&&}&&
public class CellLayout extends ViewGroup {
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public int cellX;
//X方向的单元格索引
public int cellY;
//Y方向的单元格索引
public int cellHS
//水平方向所占高度
public int cellVS
//垂直方向所占高度
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
cellHSpan = 1;
//默认为高度 1
cellVSpan = 1;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source); //默认为高度 1
cellHSpan = 1;
cellVSpan = 1;
public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
this.cellX = cellX;
this.cellY = cellY;
this.cellHSpan = cellHS
this.cellVSpan = cellVS
& &对该自定义CellLayout.LayoutParams类的使用可以参考LinearLayout.LayoutParams类,我也不再赘述了。
&方法2流程分析:
使用属性android:layout_heigth=””以及android:layout_weight=””
时,为某个View设置LayoutParams值。
&其实这种赋值方法其实也如同前面那种,只不过它需要一个前期孵化过程---需要利用XML解析将布局文件
解析成一个完整的View树,可别小看它了,所有Xxx.xml的布局文件都需要解析成一个完整的View树。下面,
我们就来仔细走这个过程,重点关注如下两个方面
&①、xml布局是如何解析成View树的 ;
&②、android:layout_heigth=””和android:layout_weight=””的解析。
一直以来,我都想当然android:layout_heigth以及android:layout_weight这两个属性的解析过程是在
&View.java内部完成的,但当我真正去找寻时,却一直没有在View.java类或者ViewGroup.java类找到。直到一位
&网友的一次提问,才发现它们的藏身之地。
3、布局文件解析流程分析
&解析布局文件时,使用的类为LayoutInflater。
关于该类的使用请参考如下博客:
& & 主要有如下API方法:
&&public&View&inflate&(XmlPullParser&parser,&ViewGroup&root,
boolean attachToRoot)
&&public&View&inflate&(int
resource,&ViewGroup&root)
&&public&View&inflate&(int
resource,&ViewGroup&root, boolean
attachToRoot)
& &这三个类主要迷惑之处在于地三个参数attachToRoot,即是否将该View树添加到root中去。具体可看这篇博客:
当然还有LayoutInflater的inflate()的其他重载方法,大家可以自行了解下。
& 我利用下面的例子给大家走走这个流程 :
public&class&MainActivity&extends&Activity&{&&
&&&&@Override&&
&&&&public&void&onCreate(Bundle&savedInstanceState)&{&&
&&&&&&&&super.onCreate(savedInstanceState);&&
&&&&&&&&//1、该方法最终也会调用到&LayoutInflater的inflate()方法中去解析。
&&&&&&&&setContentView(R.layout.main);&&
&&&&&&&&&&
&&&&&&&&//2、使用常见的API方法去解析xml布局文件,
&&&&&&&&LayoutInflater&layoutInflater&=&(LayoutInflater)getSystemService();&&
&&&&&&&&View&root&=&layoutInflater.inflate(R.layout.main,&null);&&
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1、该方法最终也会调用到 LayoutInflater的inflate()方法中去解析。
setContentView(R.layout.main);
//2、使用常见的API方法去解析xml布局文件,
LayoutInflater layoutInflater = (LayoutInflater)getSystemService();
View root = layoutInflater.inflate(R.layout.main, null);
1、获得LayoutInflater的引用。
&路径:\frameworks\base\core\java\android\app\ContextImpl.java
class&ContextImpl&extends&Context&{&&
&&&&if&(WINDOW_SERVICE.equals(name))&{&&
&&&&&&&&return&WindowManagerImpl.getDefault();&&
&&&&}&else&if&(LAYOUT_INFLATER_SERVICE.equals(name))&{&&
&&&&&&&&synchronized&(mSync)&{&&
&&&&&&&&&&&&LayoutInflater&inflater&=&mLayoutI&&
&&&&&&&&&&&&//是否已经赋值,如果是,直接返回引用
&&&&&&&&&&&&if&(inflater&!=&null)&{&&
&&&&&&&&&&&&&&&&return&&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&//返回一个LayoutInflater对象,getOuterContext()指的是我们的Activity、Service或者Application引用
&&&&&&&&&&&&mLayoutInflater&=&inflater&=&PolicyManager.makeNewLayoutInflater(getOuterContext());&&
&&&&&&&&&&&&return&&&
&&&&&&&&}&&
&&&&}&else&if&(ACTIVITY_SERVICE.equals(name))&{&&
&&&&&&&&return&getActivityManager();&&
&&&&}...&&
class ContextImpl extends Context {
if (WINDOW_SERVICE.equals(name)) {
return WindowManagerImpl.getDefault();
} else if (LAYOUT_INFLATER_SERVICE.equals(name)) {
synchronized (mSync) {
LayoutInflater inflater = mLayoutI
//是否已经赋值,如果是,直接返回引用
if (inflater != null) {
//返回一个LayoutInflater对象,getOuterContext()指的是我们的Activity、Service或者Application引用
mLayoutInflater = inflater = PolicyManager.makeNewLayoutInflater(getOuterContext());
} else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
& &继续去PolicyManager查询对应函数,看看内部实现。
& &路径:frameworks\base\core\java\com\android\internal\policy\PolicyManager.java
public&final&class&PolicyManager&{&&
&&&&private&static&final&String&POLICY_IMPL_CLASS_NAME&=&"com.android.internal.policy.impl.Policy";&&
&&&&private&static&final&IPolicy&sP&&&//&这可不是Binder机制额,这只是是一个接口,别想多啦
&&&&static&{&&
&&&&&&&&//&Pull&in&the&actual&implementation&of&the&policy&at&run-time
&&&&&&&&try&{&&
&&&&&&&&&&&&Class&policyClass&=&Class.forName(POLICY_IMPL_CLASS_NAME);&&
&&&&&&&&&&&&sPolicy&=&(IPolicy)policyClass.newInstance();&&
&&&&&&&&}&&
&&&&&&&&...&&
&&&&public&static&LayoutInflater&makeNewLayoutInflater(Context&context)&{&&
&&&&&&&&return&sPolicy.makeNewLayoutInflater(context);&//继续去实现类中去查找
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME = "com.android.internal.policy.impl.Policy";
private static final IPolicy sP
// 这可不是Binder机制额,这只是是一个接口,别想多啦
// Pull in the actual implementation of the policy at run-time
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context); //继续去实现类中去查找
& IPolicy接口的实现对为Policy类。路径:/frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
//Simple&implementation&of&the&policy&interface&that&spawns&the&right
//set&of&objects
public&class&Policy&implements&IPolicy{&&
&&&&public&PhoneLayoutInflater&makeNewLayoutInflater(Context&context)&{&&
&&&&&&&&//实际上返回的是PhoneLayoutInflater类。
&&&&&&&&return&new&PhoneLayoutInflater(context);&&
//PhoneLayoutInflater继承至LayoutInflater类
public&class&PhoneLayoutInflater&extends&LayoutInflater&{&&
&&&&public&PhoneLayoutInflater(Context&context)&{&&
&&&&&&&&super(context);&&
//Simple implementation of the policy interface that spawns the right
//set of objects
public class Policy implements IPolicy{
public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
//实际上返回的是PhoneLayoutInflater类。
return new PhoneLayoutInflater(context);
//PhoneLayoutInflater继承至LayoutInflater类
public class PhoneLayoutInflater extends LayoutInflater {
public PhoneLayoutInflater(Context context) {
super(context);
&LayoutInflater是个抽象类,实际上我们返回的是PhoneLayoutInflater类,但解析过程的操作基本上是在
LayoutInflater中完成地。
2、调用inflate()方法去解析布局文件。
public&abstract&class&LayoutInflater&{&&
&&&&public&View&inflate(int&resource,&ViewGroup&root)&{&&
&&&&&&&&//继续看下个函数,注意root为null
&&&&&&&&return&inflate(resource,&root,&root&!=&null);&&&
&&&&public&View&inflate(int&resource,&ViewGroup&root,&boolean&attachToRoot)&{&&
&&&&&&&&//获取一个XmlResourceParser来解析XML文件---布局文件。
&&&&&&&&//XmlResourceParser类以及xml是如何解析的,大家自己有兴趣找找。
&&&&&&&&XmlResourceParser&parser&=&getContext().getResources().getLayout(resource);&&
&&&&&&&&try&{&&
&&&&&&&&&&&&return&inflate(parser,&root,&attachToRoot);&&
&&&&&&&&}&finally&{&&
&&&&&&&&&&&&parser.close();&&
&&&&&&&&}&&
public&interface&XmlResourceParser&extends&XmlPullParser,&AttributeSet&{&&
&&&&public&void&close();&&
public abstract class LayoutInflater {
public View inflate(int resource, ViewGroup root) {
//继续看下个函数,注意root为null
return inflate(resource, root, root != null);
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
//获取一个XmlResourceParser来解析XML文件---布局文件。
//XmlResourceParser类以及xml是如何解析的,大家自己有兴趣找找。
XmlResourceParser parser = getContext().getResources().getLayout(resource);
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
public interface XmlResourceParser extends XmlPullParser, AttributeSet {
public void close();
我们获得了一个当前应用程序环境的XmlResourceParser对象,该对象的主要作用就是来解析xml布局文件的。
&&XmlResourceParser类是个接口类,更多关于XML解析的,大家可以参考下面博客:
&Step 3 、真正地开始解析工作
public&abstract&class&LayoutInflater&{&&
&&&&//我们传递过来的参数如下:&root&为null&,&attachToRoot为false&。
&&&&public&View&inflate(XmlPullParser&parser,&ViewGroup&root,&boolean&attachToRoot)&{&&
&&&&&&&&synchronized&(mConstructorArgs)&{&&
&&&&&&&&&&&&final&AttributeSet&attrs&=&Xml.asAttributeSet(parser);&&
&&&&&&&&&&&&Context&lastContext&=&(Context)mConstructorArgs[0];&&
&&&&&&&&&&&&mConstructorArgs[0]&=&mC&&//该mConstructorArgs属性最后会作为参数传递给View的构造函数
&&&&&&&&&&&&View&result&=&&&//根View
&&&&&&&&&&&&try&{&&
&&&&&&&&&&&&&&&&//&Look&for&the&root&node.
&&&&&&&&&&&&&&&&int&&&
&&&&&&&&&&&&&&&&while&((type&=&parser.next())&!=&XmlPullParser.START_TAG&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&type&!=&XmlPullParser.END_DOCUMENT)&{&&
&&&&&&&&&&&&&&&&&&&&//&Empty
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&...&&
&&&&&&&&&&&&&&&&final&String&name&=&parser.getName();&&//节点名,即API中的控件或者自定义View完整限定名。
&&&&&&&&&&&&&&&&if&(TAG_MERGE.equals(name))&{&//&处理标签
&&&&&&&&&&&&&&&&&&&&if&(root&==&null&||&!attachToRoot)&{&&
&&&&&&&&&&&&&&&&&&&&&&&&throw&new&InflateException("&can&be&used&only&with&a&valid&"&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+&"ViewGroup&root&and&attachToRoot=true");&&
&&&&&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&&&&&//将标签的View树添加至root中,该函数稍后讲到。
&&&&&&&&&&&&&&&&&&&&rInflate(parser,&root,&attrs);&&
&&&&&&&&&&&&&&&&}&else&{&&
&&&&&&&&&&&&&&&&&&&&//&Temp&is&the&root&view&that&was&found&in&the&xml
&&&&&&&&&&&&&&&&&&&&//创建该xml布局文件所对应的根View。
&&&&&&&&&&&&&&&&&&&&View&temp&=&createViewFromTag(name,&attrs);&&&
&&&&&&&&&&&&&&&&&&&&ViewGroup.LayoutParams&params&=&null;&&
&&&&&&&&&&&&&&&&&&&&if&(root&!=&null)&{&&
&&&&&&&&&&&&&&&&&&&&&&&&//&Create&layout&params&that&match&root,&if&supplied
&&&&&&&&&&&&&&&&&&&&&&&&//根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。
&&&&&&&&&&&&&&&&&&&&&&&&params&=&root.generateLayoutParams(attrs);&&&
&&&&&&&&&&&&&&&&&&&&&&&&if&(!attachToRoot)&{&//重新设置temp的LayoutParams
&&&&&&&&&&&&&&&&&&&&&&&&&&&&//&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
&&&&&&&&&&&&&&&&&&&&//添加所有其子节点,即添加所有字View
&&&&&&&&&&&&&&&&&&&&rInflate(parser,&temp,&attrs);&&
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&//&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&=&&&
&&&&&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&}&&&
&&&&&&&&&&&&...&&
&&&&&&&&&&&&return&&&
&&&&&&&&}&&
&&&&View&createViewFromTag(String&name,&AttributeSet&attrs)&{&&
&&&&&&&&//节点是否为View,如果是将其重新赋值,形如&
&&&&&&&&if&(name.equals("view"))&{&&&&
&&&&&&&&&&&&name&=&attrs.getAttributeValue(null,&"class");&&
&&&&&&&&}&&
&&&&&&&&try&{&&
&&&&&&&&&&&&View&view&=&(mFactory&==&null)&?&null&:&mFactory.onCreateView(name,&&
&&&&&&&&&&&&&&&&&&&&mContext,&attrs);&&//没有设置工厂方法
&&&&&&&&&&&&if&(view&==&null)&{&&
&&&&&&&&&&&&&&&&//通过这个判断是Android&API的View,还是自定义View
&&&&&&&&&&&&&&&&if&(-1&==&name.indexOf('.'))&{&&
&&&&&&&&&&&&&&&&&&&&view&=&onCreateView(name,&attrs);&//创建Android&API的View实例
&&&&&&&&&&&&&&&&}&else&{&&
&&&&&&&&&&&&&&&&&&&&view&=&createView(name,&null,&attrs);//创建一个自定义View实例
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&return&&&
&&&&&&&&}&&&
&&&&&&&&...&&
&&&&//获得具体视图的实例对象
&&&&public&final&View&createView(String&name,&String&prefix,&AttributeSet&attrs)&{&&
&&&&&&&&Constructor&constructor&=&sConstructorMap.get(name);&&
&&&&&&&&Class&clazz&=&null;&&
&&&&&&&&//以下功能主要是获取如下三个类对象:
&&&&&&&&//1、类加载器&&ClassLoader
&&&&&&&&//2、Class对象
&&&&&&&&//3、类的构造方法句柄&Constructor
&&&&&&&&try&{&&
&&&&&&&&&&&&if&(constructor&==&null)&{&&
&&&&&&&&&&&&//&Class&not&found&in&the&cache,&see&if&it's&real,&and&try&to&add&it
&&&&&&&&&&&&clazz&=&mContext.getClassLoader().loadClass(prefix&!=&null&?&(prefix&+&name)&:&name);&&
&&&&&&&&&&&&...&&
&&&&&&&&&&&&constructor&=&clazz.getConstructor(mConstructorSignature);&&
&&&&&&&&&&&&sConstructorMap.put(name,&constructor);&&
&&&&&&&&}&else&{&&
&&&&&&&&&&&&//&If&we&have&a&filter,&apply&it&to&cached&constructor
&&&&&&&&&&&&if&(mFilter&!=&null)&{&&
&&&&&&&&&&&&&&&&...&&&&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&&&&&//传递参数获得该View实例对象
&&&&&&&&&&&&Object[]&args&=&mConstructorA&&
&&&&&&&&&&&&args[1]&=&&&
&&&&&&&&&&&&return&(View)&constructor.newInstance(args);&&
&&&&&&&&}&&&
&&&&&&&&...&&
public abstract class LayoutInflater {
//我们传递过来的参数如下: root 为null , attachToRoot为false 。
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mC
//该mConstructorArgs属性最后会作为参数传递给View的构造函数
View result =
// Look for the root node.
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
final String name = parser.getName();
//节点名,即API中的控件或者自定义View完整限定名。
if (TAG_MERGE.equals(name)) { // 处理标签
if (root == null || !attachToRoot) {
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
//将标签的View树添加至root中,该函数稍后讲到。
rInflate(parser, root, attrs);
// Temp is the root view that was found in the xml
//创建该xml布局文件所对应的根View。
View temp = createViewFromTag(name, attrs);
ViewGroup.LayoutParams params =
if (root != null) {
// Create layout params that match root, if supplied
//根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。
params = root.generateLayoutParams(attrs);
if (!attachToRoot) { //重新设置temp的LayoutParams
// 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
//添加所有其子节点,即添加所有字View
rInflate(parser, temp, attrs);
// 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) {
View createViewFromTag(String name, AttributeSet attrs) {
//节点是否为View,如果是将其重新赋值,形如
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
View view = (mFactory == null) ? null : mFactory.onCreateView(name,
mContext, attrs);
//没有设置工厂方法
if (view == null) {
//通过这个判断是Android API的View,还是自定义View
if (-1 == name.indexOf('.')) {
view = onCreateView(name, attrs); //创建Android API的View实例
view = createView(name, null, attrs);//创建一个自定义View实例
//获得具体视图的实例对象
public final View createView(String name, String prefix, AttributeSet attrs) {
Constructor constructor = sConstructorMap.get(name);
Class clazz =
//以下功能主要是获取如下三个类对象:
//1、类加载器
ClassLoader
//2、Class对象
//3、类的构造方法句柄 Constructor
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name);
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
//传递参数获得该View实例对象
Object[] args = mConstructorA
return (View) constructor.newInstance(args);
这段代码的作用是获取xml布局文件的root View,做了如下两件事情
1、获取xml布局的View实例,通过createViewFromTag()方法获取,该方法会判断节点名是API
还是自定义控件,继而调用合适的方法去实例化View。
& 2、判断root以及attachToRoot参数,重新设置root
View值以及temp变量的LayoutParams值。
&如果仔细看着段代码,不知大家心里有没有疑惑:当root为null时,我们的temp变量的LayoutParams值是为
null的,即它不会被赋值?有个View的LayoutParams值为空,那么,在系统中不会报异常吗?见下面部分
//我们传递过来的参数如下:&root&为null&,&attachToRoot为false&。
public&View&inflate(XmlPullParser&parser,&ViewGroup&root,&boolean&attachToRoot)&{&&
&&&&synchronized&(mConstructorArgs)&{&&
&&&&&&&&...&&
&&&&&&&&try&{&&
&&&&&&&&&&&&&&
&&&&&&&&&&&&...&&
&&&&&&&&&&&&if&(TAG_MERGE.equals(name))&{&//&处理标签
&&&&&&&&&&&&&&&&...&&
&&&&&&&&&&&&}&else&{&&
&&&&&&&&&&&&&&&&//&Temp&is&the&root&view&that&was&found&in&the&xml
&&&&&&&&&&&&&&&&//创建该xml布局文件所对应的根View。
&&&&&&&&&&&&&&&&View&temp&=&createViewFromTag(name,&attrs);&&&
&&&&&&&&&&&&&&&&ViewGroup.LayoutParams&params&=&null;&&
&&&&&&&&&&&&&&&&//注意!!!&root为null时,temp变量的LayoutParams属性不会被赋值的。
&&&&&&&&&&&&&&&&if&(root&!=&null)&{&&
&&&&&&&&&&&&&&&&&&&&//&Create&layout&params&that&match&root,&if&supplied
&&&&&&&&&&&&&&&&&&&&//根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。
&&&&&&&&&&&&&&&&&&&&params&=&root.generateLayoutParams(attrs);&&&
&&&&&&&&&&&&&&&&&&&&if&(!attachToRoot)&{&//重新设置temp的LayoutParams
&&&&&&&&&&&&&&&&&&&&&&&&//&Set&the&layout&params&for&temp&if&we&are&not
&&&&&&&&&&&&&&&&&&&&&&&&//&attaching.&(If&we&are,&we&use&addView,&below)
&&&&&&&&&&&&&&&&&&&&&&&&temp.setLayoutParams(params);&&
&&&&&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&...&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&&
&&&&&&&&...&&
//我们传递过来的参数如下: root 为null , attachToRoot为false 。
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
if (TAG_MERGE.equals(name)) { // 处理标签
// Temp is the root view that was found in the xml
//创建该xml布局文件所对应的根View。
View temp = createViewFromTag(name, attrs);
ViewGroup.LayoutParams params =
//注意!!! root为null时,temp变量的LayoutParams属性不会被赋值的。
if (root != null) {
// Create layout params that match root, if supplied
//根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。
params = root.generateLayoutParams(attrs);
if (!attachToRoot) { //重新设置temp的LayoutParams
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
关于这个问题的详细答案,我会在后面讲到。这儿我简单说下,任何View树的顶层View被添加至窗口时,
一般调用WindowManager.addView()添加至窗口时,在这个方法中去做进一步处理。即使,LayoutParams
值为空,UI框架每次measure()时都忽略该View的LayoutParams值,而是直接传递MeasureSpec值至View树。
&接下来,我们关注另外一个函数,rInflate(),该方法会递归调用每个View下的子节点,以当前View作为根View
&形成一个View树。
//递归调用每个字节点
private&void&rInflate(XmlPullParser&parser,&View&parent,&final&AttributeSet&attrs)&&
&&&&&&&&throws&XmlPullParserException,&IOException&{&&
&&&&final&int&depth&=&parser.getDepth();&&
&&&&int&&&
&&&&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();&&
&&&&&&&&&&
&&&&&&&&if&(TAG_REQUEST_FOCUS.equals(name))&{&//处理标签
&&&&&&&&&&&&parseRequestFocus(parser,&parent);&&
&&&&&&&&}&else&if&(TAG_INCLUDE.equals(name))&{&//处理标签
&&&&&&&&&&&&if&(parser.getDepth()&==&0)&{&&
&&&&&&&&&&&&&&&&throw&new&InflateException("&cannot&be&the&root&element");&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&parseInclude(parser,&parent,&attrs);//解析节点
&&&&&&&&}&else&if&(TAG_MERGE.equals(name))&{&//处理标签
&&&&&&&&&&&&throw&new&InflateException("&must&be&the&root&element");&&
&&&&&&&&}&else&{&&
&&&&&&&&&&&&//根据节点名构建一个View实例对象
&&&&&&&&&&&&final&View&view&=&createViewFromTag(name,&attrs);&&&
&&&&&&&&&&&&final&ViewGroup&viewGroup&=&(ViewGroup)&&&
&&&&&&&&&&&&//调用generateLayoutParams()方法返回一个LayoutParams实例对象,
&&&&&&&&&&&&final&ViewGroup.LayoutParams&params&=&viewGroup.generateLayoutParams(attrs);&&
&&&&&&&&&&&&rInflate(parser,&view,&attrs);&//继续递归调用
&&&&&&&&&&&&viewGroup.addView(view,&params);&//OK,将该View以特定LayoutParams值添加至父View中
&&&&&&&&}&&
&&&&parent.onFinishInflate();&&//完成了解析过程,通知....
//递归调用每个字节点
private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() & depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) { //处理标签
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) { //处理标签
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
parseInclude(parser, parent, attrs);//解析节点
} else if (TAG_MERGE.equals(name)) { //处理标签
throw new InflateException(" must be the root element");
//根据节点名构建一个View实例对象
final View view = createViewFromTag(name, attrs);
final ViewGroup viewGroup = (ViewGroup)
//调用generateLayoutParams()方法返回一个LayoutParams实例对象,
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs); //继续递归调用
viewGroup.addView(view, params); //OK,将该View以特定LayoutParams值添加至父View中
parent.onFinishInflate();
//完成了解析过程,通知....
值得注意的是,每次addView前都调用了viewGroup.generateLayoutParams(attrs)去构建一个LayoutParams
实例,然后在addView()方法中为其赋值。参见如下代码:ViewGroup.java
public&abstract&class&ViewGroup&extends&View&implements&ViewParent,&ViewManager&{&&
&&&&public&LayoutParams&generateLayoutParams(AttributeSet&attrs)&{&&
&&&&&&&&return&new&LayoutParams(getContext(),&attrs);&&
&&&&public&static&class&LayoutParams&{&&
&&&&&&&&...&//会调用这个构造函数
&&&&&&&&public&LayoutParams(Context&c,&AttributeSet&attrs)&{&&
&&&&&&&&&&&&TypedArray&a&=&c.obtainStyledAttributes(attrs,&R.styleable.ViewGroup_Layout);&&
&&&&&&&&&&&&setBaseAttributes(a,&&
&&&&&&&&&&&&&&&&&&&&R.styleable.ViewGroup_Layout_layout_width,&&
&&&&&&&&&&&&&&&&&&&&R.styleable.ViewGroup_Layout_layout_height);&&
&&&&&&&&&&&&a.recycle();&&
&&&&&&&&}&&
&&&&&&&&protected&void&setBaseAttributes(TypedArray&a,&int&widthAttr,&int&heightAttr)&{&&
&&&&&&&&&&&&width&=&a.getLayoutDimension(widthAttr,&"layout_width");&&
&&&&&&&&&&&&height&=&a.getLayoutDimension(heightAttr,&"layout_height");&&
&&&&&&&&}&&
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
public static class LayoutParams {
... //会调用这个构造函数
public LayoutParams(Context c, AttributeSet attrs) {
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
setBaseAttributes(a,
R.styleable.ViewGroup_Layout_layout_width,
R.styleable.ViewGroup_Layout_layout_height);
a.recycle();
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
width = a.getLayoutDimension(widthAttr, "layout_width");
height = a.getLayoutDimension(heightAttr, "layout_height");
我们还是探寻根底,去TypeArray类的getLayoutDimension()看看。
&路径:/frameworks/base/core/java/android/content/res/TypedArray.java
public&class&TypedArray&{&&
&&&&public&int&getLayoutDimension(int&index,&String&name)&{&&
&&&&&&&&index&*=&AssetManager.STYLE_NUM_ENTRIES;&&
&&&&&&&&final&int[]&data&=&mD&&
&&&&&&&&//获得属性对应的标识符&,&Identifies,目前还没有仔细研究相关类。
&&&&&&&&final&int&type&=&data[index+AssetManager.STYLE_TYPE];&&
&&&&&&&&if&(type&&=&TypedValue.TYPE_FIRST_INT&&
&&&&&&&&&&&&&&&&&&&type&&=&TypedValue.TYPE_LAST_INT)&{&&
&&&&&&&&&&&&return&data[index+AssetManager.STYLE_DATA];&&
&&&&&&&&}&else&if&(type&==&TypedValue.TYPE_DIMENSION)&{&//类型为dimension类型
&&&&&&&&&&&&return&TypedValue.complexToDimensionPixelSize(&&
&&&&&&&&&&&&&&&&data[index+AssetManager.STYLE_DATA],&mResources.mMetrics);&&
&&&&&&&&}&&
&&&&&&&&//没有提供layout_weight和layout_height会来到此处&,这儿会报异常!
&&&&&&&&//因此布局文件中的View包括自定义View必须加上属性layout_weight和layout_height。
&&&&&&&&throw&new&RuntimeException(getPositionDescription()&&
&&&&&&&&&&&&&&&&+&":&You&must&supply&a&"&+&name&+&"&attribute.");&&
public class TypedArray {
public int getLayoutDimension(int index, String name) {
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mD
//获得属性对应的标识符 , Identifies,目前还没有仔细研究相关类。
final int type = data[index+AssetManager.STYLE_TYPE];
if (type &= TypedValue.TYPE_FIRST_INT
&& type &= TypedValue.TYPE_LAST_INT) {
return data[index+AssetManager.STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) { //类型为dimension类型
return TypedValue.complexToDimensionPixelSize(
data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
//没有提供layout_weight和layout_height会来到此处 ,这儿会报异常!
//因此布局文件中的View包括自定义View必须加上属性layout_weight和layout_height。
throw new RuntimeException(getPositionDescription()
+ ": You must supply a " + name + " attribute.");
& &从上面得知,
&我们将View的AttributeSet属性传递给generateLayoutParams()方法,让其构建合适地
&LayoutParams对象,并且初始化属性值weight和height。同时我们也得知&布局文件中的View包括自定义View
&必须加上属性layout_weight和layout_height,否则会报异常。
& Step 3 主要做了如下事情:
&首先,获得了了布局文件地root
View,即布局文件中最顶层的View。
&其次,通过递归调用,我们形成了整个View树以及设置了每个View的LayoutParams对象。
&&总结:通过对布局文件的解析流程的学习,也就是转换为View树的过程,我们明白了解析过程的个中奥妙,以及
设置ViewLayoutParams对象的过程。但是,我们这儿只是简单的浮光掠影,更深层次的内容希望大家能深入学习。
本来是准备接下去往下写的,但无奈贴出来的代码太多,文章有点长而且自己也有点凌乱了,因此决定做两篇
博客发表吧。下篇内容包括如下方面:
1、MeasureSpec类说明 ;
2、measure过程中如何正确设置每个View的长宽 ;
3、UI框架正确设置顶层View的LayoutParams对象,对Activity而言,顶层View则是DecorView,
&其他的皆是普通View了。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 xml解析 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信