xml文件的根节点layout_width或者layout_height设置无效果的原因分析

在android开发中相信大家对ListView、GridView等组建都很熟悉,在使用它们的时候需要自己配置相关的Adapter,并且配置现骨干的xml文件作为ListView等组建的子View,这些xml文件在Adapter的getView方法中调用。例如:

public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView==null) {
            convertView = App.getLayoutInflater().inflate(R.layout.item, null);
        }

        return convertView;
    }

item.xml文件如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="#008000"
     >
        <ImageView
            android:src="@drawable/ic_launcher"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </ImageView>
</RelativeLayout>

用上面的方法会发现无论根View也就是RelativeLayout的layout_width和layout_height设置多大,运行效果始终都是一样的;也就是说此时你想通过RelativeLayout来改变里面子ImageView的大小是行不通的,通常用的解决方式就是里面在添加一个View把ImageView包裹起来,通过设置该View的大小来改变ImageView的大小(注意不一定是ImageView,也可能里面包含了若干个view):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="#008000"
     >
    <RelativeLayout
        android:layout_width="900dp"
        android:layout_height="200dp"
        android:background="@android:color/black">
        <ImageView
            android:src="@drawable/ic_launcher"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </ImageView>
    </RelativeLayout>

</RelativeLayout>

虽然找到了解决方法,但是知其然还要知其所以然,为什么好这样呢?在分析之前要弄清一个概念性的问题:

layout_width不是width!layout_height不是height!也就是说这两个属性设置的并不是View的宽和高,layout是布局的意思,也即是说这两个属性是View在布局中的宽和高!既然是布局,肯定都有个放置该View的地方,也就是说有一个媒介来放置View,并且在该媒介上划出一个layout_width和layout_heigth的大小来放置该View。如果没有该媒介View布局在哪儿呢?所以说为了上面的问题的根本原因就是因为你没有为xml文件设置一个布局媒介(该媒介也是个View,也即是rootView),所以为了保障你的item.xml中根View的layout_width和layout_heigth能起作用,需要设置一个这样的媒介。代码inflate(int,ViewGroup
root)中这个root就是这样的一个媒介,但是通常传递的都是null,所以item.xml文件的根View是没有媒介作为布局依据的,所以不起作用;既然问题的原因找到了那么就可以用一个笨的方法来解决这个问题:为inflate提供一个root,在代码里面我简单的做了一下处理,验证了自己的想法:

	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView==null) {
			convertView = App.getLayoutInflater().inflate(R.layout.item, null);
                        //手动设置一个root,此时在设置layout_width或者layout_height就会起作用了
                        convertView = App.getLayoutInflater().inflate(R.layout.item, (ViewGroup)convertView);
		}

		return convertView;
	}

之所以是本方法,因为它把item做了两次的xml解析,因为inflate调用了两次。当然此时的item是:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="#008000"
     >
        <ImageView
            android:src="@drawable/ic_launcher"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </ImageView>
</RelativeLayout>

既然跟inflate第二个参数root有关,那么看看root都干了些什么,追踪源代码最终解析xml的方法代码如下:

//切记,此时root为null,attachToRoot在源码中为root!=null,所以此处为false
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ......
            View result = root;  //根View ,为null
            try {
                 ....
                final String name = parser.getName();  //节点名,如果是自定义View的话 就是全限定名
               //该if语句暂不用看
               if (TAG_MERGE.equals(name)) { // 处理<merge />标签
                   ...
                } else {
                    // Temp is the root view that was found in the xml
                    //这个是xml文件对应的那个根View,在item.xml文件中就是RelativeLayout
                    View temp = createViewFromTag(name, attrs);   

                    ViewGroup.LayoutParams params = null;
                    //因为root==null,if条件不成立
                    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
                    //遍历temp下所有的子节点,也就是xml文件跟文件中的所有字View
                    rInflate(parser, temp, attrs);  

                    //把xml文件的根节点以及根节点中的子节点的view都添加到root中,当然此时root等于null的情况下此处是不执行的
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }
                    //如果根节点为null,就直接返回xml中根节点以及根节点的子节点组成的View
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }
            }
            ...
            return result;
        }
    }

通过分析上面的代码我们可以发现:当root为null的时候,inflate直接返回的是xml文件生成的View,此时返回的View根View就是xml文件的根节点。此时该View没有存在任何的布局中,所以根节点的layout_height和layout_width没有依附的媒介,导致设置这两个属性是无效的。但是如果root不为null的话,inflate返回的View是这么一个View,首先把xml解析成一个View,然后把该View通过root.addView添加到root里面去,然后直接返回root。也就是说此时xml文件中的根节点的layout_width和layout_height就是在root中的布局的宽和高,所以此时这两个属性是有效果的。

注意,在Activity中我们经常调用setContentView(int layoutResourceId)来设置Activity的View,,为什么这些xml文件根节点的layout_width和layout_height有效果呢?且看下面的分析,分析源码发现setContentView实际上也是调用了inflate方法来对xml文件进行解析:

    //Activity中有一个window引用,在Activity的setContentView中会调用window.setContentView,该引用指向的对象是PhoneWindow,此段代码就是PhoneWindow类中的代码
@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            //该方法中会对mContentParent进行初始化,保证它不会null
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
       //此处代码熟悉了吧,正式像上面的Adapter中一样,只不过此时root不为null!!!!
       mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }

可以发现该方法中有一个mContentParent,首先判断mContentParent是否为null,如果为null的话会在installDecor()方法中对它进行初始化,也就是说mContentParent始终可以得到初始化,紧接着会调用inflate(layoutResId,mContentParent)方法,可以看到此时inflate中第二个代表root的参数为mContentparent,且不为null。然后执行该方法对该xml文件进行解析,最终还会执行上面的inlfate(XmlPullParser,root,attachToRoot)方法中去,根据上面的分析,所以可得出结论在Activity中xml的根节点设置layout_width或者layout_height是有效果的!

时间: 2024-08-08 09:40:37

xml文件的根节点layout_width或者layout_height设置无效果的原因分析的相关文章

C# XML 给xml文件添加根节点

1 代码 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Xml.Linq; 7 8 namespace ConsoleApplication8 9 { 10 class Program 11 { 12 static void Main(string[] args)

读取XML文件的指定节点的值 并转换为Item

cmb_State_Send.ItemsSource = null; XmlDocument doc = new XmlDocument(); doc.Load("D:\\模板\\WorkstationState_Config.xml"); //加载Xml文件 XmlElement rootElem = doc.DocumentElement; //获取根节点 XmlNode xn = rootElem.SelectSingleNode("//Workstation[@Nam

SQLServer 从xml 文件中提取节点数据到数据库中

原文出处:http://blog.csdn.net/kk185800961/article/details/12350715  转载请注明出处 XML实例文件: <?xml version="1.0" encoding="utf-8"?> <!-- edited with XMLSpy v2010 (http://www.altova.com) by fengshuai (founder) --> <Root> <Frame

java DOM解析xml文件出现#text节点解决方案

java DOM解析xml文件出现#text节点解决方案 原生java解析xml的方法提供了两个,DocumentBuilder 和 SAXParser. 试了第一个DOM方法,在解析输出节点过程中,getNodeName()输出节点名发现多出了几个#text节点. text.xml <?xml version="1.0" encoding="UTF-8"?> <!-- xml解析测试文件 --> <LData> <ldat

C#中操作xml文件(插入节点、修改、删除)

已知有一个XML文件(bookstore.xml)如下:     <?xml   version="1.0"   encoding="gb2312"?>     <bookstore>         <book   genre="fantasy"   ISBN="2-3631-4">             <title>Oberon's   Legacy</title&

Tomcat中server.xml文件内各节点详解

由于 Tomcat 基于 Java,实际上在各种 Linux 发行版里的配置方法都大同小异,只是我看见在 Arch Linux 环境里搭建 Tomcat 的文章比较少,所以在 Arch Linux 实践一遍然后得出此文.此文假设你对 Tomcat 并不十分了解,主要笔墨将会在 Tomcat 的配置之上. 1.安装 Java JDK 在 Arch Linux 的 extra 源里有 jdk7-openjdk 和 openjdk6,可以直接使用 pacman 安装,至于选用哪一个可以视具体情况而定.

转 Tomcat中server.xml文件内各节点详解

由于 Tomcat 基于 Java,实际上在各种 Linux 发行版里的配置方法都大同小异,只是我看见在 Arch Linux 环境里搭建 Tomcat 的文章比较少,所以在 Arch Linux 实践一遍然后得出此文.此文假设你对 Tomcat 并不十分了解,主要笔墨将会在 Tomcat 的配置之上. 1.安装 Java JDK 在 Arch Linux 的 extra 源里有 jdk7-openjdk 和 openjdk6,可以直接使用 pacman 安装,至于选用哪一个可以视具体情况而定.

C# XML 输出xml根节点下的直接(第一级)子节点所有的属性的名字和值

1 xml文件内容 1 <?xml version="1.0" encoding="utf-8" ?> 2 <cultures> 3 4 <daojia count="100" comment="good"> 5 <book> 6 <name> 道德经</name> 7 <author>老子</author> 8 </book

C# XML 输出xml根节点的第二级子节点的内容

1 xml文件 1 <?xml version="1.0" encoding="utf-8" ?> 2 <cultures> 3 4 <daojia> 5 <book> 6 <name> 道德经</name> 7 <author>老子</author> 8 </book> 9 <book> 10 <name> 文始真经</name