Android LayoutInflater原理分析,带你一步步深入了解View(一)

转载请注明出处:

有段时间没写博客了,感觉都有些生疏了呢。最近繁忙的工作终于告一段落,又有时间写文章了,接下来还会继续坚持每一周篇的节奏。

有不少朋友跟我反应,都希望我可以写一篇关于View的文章,讲一讲View的工作原理以及自定义View的方法。没错,承诺过的文章我是一定要兑现的,而且在View这个话题上我还准备多写几篇,尽量能将这个知识点讲得透彻一些。那么今天就从LayoutInflater开始讲起吧。

相信接触Android久一点的朋友对于LayoutInflater一定不会陌生,都会知道它主要是用于加载布局的。而刚接触Android的朋友可能对LayoutInflater不怎么熟悉,因为加载布局的任务通常都是在Activity中调用setContentView()方法来完成的。其实setContentView()方法的内部也是使用LayoutInflater来加载布局的,只不过这部分源码是internal的,不太容易查看到。那么今天我们就来把LayoutInflater的工作流程仔细地剖析一遍,也许还能解决掉某些困扰你心头多年的疑惑。

先来看一下LayoutInflater的基本用法吧,它的用法非常简单,首先需要获取到LayoutInflater的实例,有两种方法可以获取到,第一种写法如下:

LayoutInflater layoutInflater = LayoutInflater.from(context);当然,还有另外一种写法也可以完成同样的效果:LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);其实第一种就是第二种的简单写法,只是Android给我们做了一下封装而已。得到了LayoutInflater的实例之后就可以调用它的inflate()方法来加载布局了,如下所示:layoutInflater.inflate(resourceId, root);inflate()方法一般接收两个参数,第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。这样就成功成功创建了一个布局的实例,之后再将它添加到指定的位置就可以显示出来了。

下面我们就通过一个非常简单的小例子,来更加直观地看一下LayoutInflater的用法。比如说当前有一个项目,其中MainActivity对应的布局文件叫做activity_main.xml,代码如下所示:

<LinearLayout xmlns:android=""android:id="@+id/main_layout"android:layout_width="match_parent"android:layout_height="match_parent" ></LinearLayout>这个布局文件的内容非常简单,只有一个空的LinearLayout,里面什么控件都没有,因此界面上应该不会显示任何东西。

那么接下来我们再定义一个布局文件,给它取名为button_layout.xml,代码如下所示:

<Button xmlns:android=""android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button" ></Button>这个布局文件也非常简单,只有一个Button按钮而已。现在我们要想办法,如何通过LayoutInflater来将button_layout这个布局添加到主布局文件的LinearLayout中。根据刚刚介绍的用法,修改MainActivity中的代码,如下所示:public class MainActivity extends Activity {private LinearLayout mainLayout;@Overrideprotected 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);}}可以看到,这里先是获取到了LayoutInflater的实例,然后调用它的inflate()方法来加载button_layout这个布局,最后调用LinearLayout的addView()方法将它添加到LinearLayout中。

现在可以运行一下程序,结果如下图所示:

Button在界面上显示出来了!说明我们确实是借助LayoutInflater成功将button_layout这个布局添加到LinearLayout中了。LayoutInflater技术广泛应用于需要动态添加View的时候,比如在ScrollView和ListView中,经常都可以看到LayoutInflater的身影。

当然,仅仅只是介绍了如何使用LayoutInflater显然是远远无法满足大家的求知欲的,知其然也要知其所以然,接下来我们就从源码的角度上看一看LayoutInflater到底是如何工作的。

不管你是使用的哪个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;}}从这里我们就可以清楚地看出,LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的。不熟悉pull解析方式的朋友可以网上搜一下,教程很多,我就不细讲了,这里我们注意看下第23行,调用了createViewFromTag()这个方法,并把节点名和参数传了进去。看到这个方法名,我们就应该能猜到,它是用于根据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。只要你扬帆,便会有八面来风。启程了,人的生命才真正开始。

Android LayoutInflater原理分析,带你一步步深入了解View(一)

相关文章:

你感兴趣的文章:

标签云: