学习自大神 郭霖 http://blog.csdn.net/guolin_blog

  LayoutInflater主要是用于Android承载布局的功能。Android中最常见的承载布局方法为Activity中调用setContentView()来完成布局。其实,setContentView()内部也是调用了LayoutInflater来加载布局的。所以,了解LayoutInflater就显得尤为重要了。

获取LayoutInflater

  1. LayoutInflater layoutInflater = LayoutInflater.from(context);

  2. LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

其实第一种就是第二种的封装方法。得到LayoutInflater后就可以调用inflater()来加载布局了。

layoutInflater.inflate(resourceId, root);

layoutInflater.inflate(resourceId, root, false);

  • 第一个参数是用来加载布id
  • 第二个参数就是在外面嵌套一个父布局root, 如果不需要则直接设为null
  • 第三个参数 boolean attachToRoot结合源码进行理解
  1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
  2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
  3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
  4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。

下面举个应用的例子:
MainAvtivity对应的布局文件为activity_main.

      

新建一个button_layout.xml

 

修改MainActivity中的代码

public class MainActivity extends Activity {        private LinearLayout mainLayout;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          mainLayout = (LinearLayout) findViewById(R.id.main_layout);          LayoutInflater layoutInflater = LayoutInflater.from(this);          View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);          mainLayout.addView(buttonLayout);      }    }   

于是便成功加载了button:

LayoutInflater广泛应用于ScrollView、ListView等动态加载布局中。

接下来我们从源码角度分析一下inflate()。不管你如何重载inflate() ,最终都会调用LayoutInflater的如下代码中:

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {      synchronized (mConstructorArgs) {          final AttributeSet attrs = Xml.asAttributeSet(parser);          mConstructorArgs[0] = mContext;          View result = root;          try {              int type;              while ((type = parser.next()) != XmlPullParser.START_TAG &&                      type != XmlPullParser.END_DOCUMENT) {              }              if (type != XmlPullParser.START_TAG) {                  throw new InflateException(parser.getPositionDescription()                          + ": No start tag found!");              }              final String name = parser.getName();              if (TAG_MERGE.equals(name)) {                  if (root == null || !attachToRoot) {                      throw new InflateException("merge can be used only with a valid ViewGroup root and attachToRoot=true");                  }                  rInflate(parser, root, attrs);              } else {                  View temp = createViewFromTag(name, attrs);                  ViewGroup.LayoutParams params = null;                  if (root != null) {                      params = root.generateLayoutParams(attrs);                      if (!attachToRoot) {                          temp.setLayoutParams(params);                      }                  }                  rInflate(parser, temp, attrs);                  if (root != null && attachToRoot) {                      root.addView(temp, params);                  }                  if (root == null || !attachToRoot) {                      result = temp;                  }              }          } catch (XmlPullParserException e) {              InflateException ex = new InflateException(e.getMessage());              ex.initCause(e);              throw ex;          } catch (IOException e) {              InflateException ex = new InflateException(parser.getPositionDescription() + ": " + e.getMessage());              ex.initCause(e);              throw ex;          }          return result;      }  } 

这里其实这就是调用了Pull方法解析了布局文件,这里注意一下createViewFromTag(name,attrs)这个方法,通过传入name节点名和attrs属性参数传入函数,根据节点名来创建View对象。

createViewFromTag(name,attrs)内部中,又会调用createView()方法,然后使用反射的方式创建出View的实例并返回。

当然,这个方法只是创建了根布局root的实例而已,接下来会在rInflate(parser, temp, attrs);中循环遍历这个根布局root下的子元素:

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)) {              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 {              final View view = createViewFromTag(name, attrs);              final ViewGroup viewGroup = (ViewGroup) parent;              final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);              rInflate(parser, view, attrs);              viewGroup.addView(view, params);          }      }      parent.onFinishInflate();  }  

可以看到,同样在createViewFromTag(name,attrs)中创建了View实例,然后递归调用rInflate()来查找该View下的子元素,每次递归玩成后就这个View添加到父布局viewGroup中。

这样,整个布局文件解析完成后就是一个完整的DOM结构,最终会把顶层的根布局返回,inflate()方法到此结束。

那么有个问题,为什么修改Botton的layout_widthlayout_height无效呢?
  • 平时我们经常使用layout_widthlayout_height来设置View的大小,并且一直都能正常工作,就好像这两个属性确实是用于设置View的大小的,而实际上则不然,它们其实是用于设置View在布局中的大小的。也就是说,首先View必须存在于一个布局中,之后如果将layout_width设置成match_parent表示让View的宽度填充满布局,如果设置成wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。这也是为什么这两个属性叫作layout_widthlayout_height,而不是width和height。

setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout


虽然setContentView()方法大家都会用,但实际上Android界面显示的原理要比我们所看到的东西复杂得多。任何一个Activity中显示的界面其实主要都由两部分组成,标题栏和内容布局。标题栏就是在很多界面顶部显示的那部分内容,比如刚刚我们的那个例子当中就有标题栏,可以在代码中控制让它是否显示。而内容布局就是一个FrameLayout,这个布局的id叫作content,我们调用setContentView()方法时所传入的布局其实就是放到这个FrameLayout中的,这也是为什么这个方法名叫作setContentView(),而不是叫setView()

更多相关文章

  1. 【Android】添加菜单和监听菜单方法详解
  2. Android(安卓)学习笔记--android――Activity加载模式
  3. Android(安卓)Interface Definition Language (AIDL) android接
  4. android SQLite 原理
  5. Android驱动使用JNI调用
  6. Android(安卓)保存和恢复activity的状态数据
  7. [转]Android(安卓)调用系统摄像头
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android主流开源自动化框架(monkeyrunner
  2. android 跑马灯 文字一行能显示全也能跑
  3. Android出现65535的原因
  4. Android网络层与数据层设计
  5. EditText编辑框
  6. Android解析自定义xml文件--Pull解析xml
  7. Android JellyBean Keyguard锁屏
  8. android webView错误处理
  9. Android中内存占用的含义:(VSS,PSS,RSS,USS)
  10. Android中各种drawable的使用