现在的位置: 首页 > 综合 > 正文

android布局文件解析成view树浅析

2013年12月06日 ⁄ 综合 ⁄ 共 5097字 ⁄ 字号 评论关闭

              

拿这个布局为例,让我们跟随eclipse进入解析xml成view树的代码;

先上一段熟悉的代码:

 

/** Called when the activity is first created. */
	@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,false);  
	}	

我们知道,在activity的onCreate里面设置布局文件的id,那么这个activity显示的就是这个布局文件。当我们设置了layout之后还是会调用LayoutInflater的inflate去解析xml文件成为view树。

 

先看看inflate函数:

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 abstract class LayoutInflater {  
    ...  
    /** 
     * Inflate a new view hierarchy from the specified XML node. Throws 
     * {@link InflateException} if there is an error. 
     */  
    //我们传递过来的参数如下: 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] = mContext;  //该mConstructorArgs属性最后会作为参数传递给View的构造函数   
            View result = root;  //根View   
  
            try {  
                // Look for the root node.   
                int type;  
                while ((type = parser.next()) != XmlPullParser.START_TAG &&  
                        type != XmlPullParser.END_DOCUMENT) {  
                    // Empty   
                }  
                ...  
                final String name = parser.getName();  //节点名,即API中的控件或者自定义View完整限定名。   
                if (TAG_MERGE.equals(name)) { // 处理<merge />标签   
                    if (root == null || !attachToRoot) {  
                        throw new InflateException("<merge /> can be used only with a valid "  
                                + "ViewGroup root and attachToRoot=true");  
                    }  
                    //将<merge />标签的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 = temp;  
                    }  
                }  
            }   
            ...  
            return result;  
        }  
    }  
      
}  

inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)先会判断root是否为null,如果是null,则先创建根节点,然后调用rInflate,否则直接调用rInflate。

 

下面我们来看一下rInflate函数

** 
 * Recursive method used to descend down the xml hierarchy and instantiate 
 * views, instantiate their children, and then call onFinishInflate(). 
 */  
//递归调用每个字节点   
private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)  
        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();  
          
        if (TAG_REQUEST_FOCUS.equals(name)) { //处理<requestFocus />标签   
            parseRequestFocus(parser, parent);  
        } else if (TAG_INCLUDE.equals(name)) { //处理<include />标签   
            if (parser.getDepth() == 0) {  
                throw new InflateException("<include /> cannot be the root element");  
            }  
            parseInclude(parser, parent, attrs);//解析<include />节点   
        } else if (TAG_MERGE.equals(name)) { //处理<merge />标签   
            throw new InflateException("<merge /> must be the root element");  
        } else {  
            //根据节点名构建一个View实例对象   
            final View view = createViewFromTag(name, attrs);   
            final ViewGroup viewGroup = (ViewGroup) parent;  
            //调用generateLayoutParams()方法返回一个LayoutParams实例对象,   
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);  
            rInflate(parser, view, attrs); //继续递归调用   
            viewGroup.addView(view, params); //OK,将该View以特定LayoutParams值添加至父View中   
        }  
    }  
    parent.onFinishInflate();  //完成了解析过程,通知....   
}  

 

这是要解析的布局

              

 

对应的布局文件为:

 

<1>
    <2>
    	<3>
        </3>
        
        <4>
        </4>
    </2>
    
     <5>
    	<6>
        </6>
        
        <7>
        </7>
    </5>
    
    <8>
    	<9>
        </9>
        
        <10>
        </10>
    </8>
    
    <11>
    	<12>
        </12>
        
        <13>
        </13>
    </11>   
   
</1>

xml文件解析成树之后的图形:

 

 

 

当前xml对应 rInflate函数解析的第一步就是控件2(1是根节点,在inflate里面已经生成),然后执行

  //根据节点名构建一个View实例对象   
            final View view = createViewFromTag(name, attrs);   
            final ViewGroup viewGroup = (ViewGroup) parent;  
            //调用generateLayoutParams()方法返回一个LayoutParams实例对象,   
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);  
            rInflate(parser, view, attrs); //继续递归调用   
            viewGroup.addView(view, params); //OK,将该View以特定LayoutParams值添加至父View中   

注明:接下来以上代码用“代码片段1”来表示

 

先是生成控件2,然后递归调用rInflate函数(把自己当成parent,和同一个parser对象传过去,这时parser已经解析过2了)

又是执行“代码片段1”,这时parent是控件2, parser.next()后得到3的attr,3也去执行“代码片段1”但是在while判断的时候由于4的深度和3是一样的(都是3),所以直接返回,于是在3执行“代码片段1”的最后一句2把3添加了进去(2是parent)并回到2。

 

这时parser.next()过了3了, 根节点调用的“代码片段1”会继续调用rInflate函数并把2作为parent传入,由于4和3一样,在生成自己的view之后调用Inflate执行while的时候发现5的深度(值为2)不比自己大,被2添加到试图之后而结束递归。

 

结束之后2被添加到了parent(就是根节点1),这时根节点调用的 “代码片段1”中只执行了一次while.

 

然后再执行一次while到5,5添加了6、7,后再被1添加,这时根节点调用的 “代码片段1”中执行了两次while.

 

然后再执行一次while到8,8添加了9、10之后被1添加,这时根节点调用的 “代码片段1”中执行了三次while.

 

然后再执行一次while到11,11添加了12、13之后被1添加。这时根节点调用的 “代码片段1”中执行了四次while.

 

最后在执行第五次while判断的时候由于

 type != XmlPullParser.END_DOCUMENT

而结束。

 

 

 

至此:整颗view树解析完毕!

抱歉!评论已关闭.