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

来源:互联网 发布:hp电池修复软件 编辑:程序博客网 时间:2024/06/08 05:31

              

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



先上一段熟悉的代码:

 

/** Called when the activity is first created. */@Overridepublic 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树解析完毕!

原创粉丝点击