layoutinflater.inflate源码详解
加载布局的两种方式
View.inflate(context,R.layout.xxx,viewGroup);
LayoutInflater.from(this).inflate(R.layout.xxx,viewGroup,true);
View.infalte内部也是调用LayoutInflater.inflate
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
//通过Context获取了一个 LayoutInflater对象
LayoutInflater factory = LayoutInflater.from(context);
//调用LayoutInflater.inflate方法
return factory.inflate(resource, root);
}
那么 LayoutInflater.inflate是怎么加载布局的呢
首先 我们先看一下LayoutInflater.inflate重载 主要是两个要注意的
public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
其他都是后面两个参数不一样而已
我们看到一个是XmlPullParser对象 和我们的布局id两个重载方法,但是在 LayoutInflater 内部其实都是调的同一个方法
比如我们如果传的是布局id 那么LayoutInflater会给我们自动创建一个XmlPullParser对象
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//=========================在这里通过我们传进来的布局id(resource) 初始化了一个XmlResourceParser=========================
final XmlResourceParser parser = res.getLayout(resource);
try {
//=================其他的方法最终都会调用这个方法加载布局==========================
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
接下来我们一行一行分析源码 日志打印之类的跳过
该方法声明
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
首先开启了线程锁 不知道的自行搜索
synchronized (mConstructorArgs)
//获取Context
final Context inflaterContext = mContext;
//获取布局的属性 比如高度 宽度 然后在createViewFromTag()方法中对View的属性初始化
final AttributeSet attrs = Xml.asAttributeSet(parser);
//不知道有啥用 知道的可以告知一下
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
//声明一个 View 默认将传进来的ViewGroup赋值给 result 后面作为返回值返回
View result = root;
主要是判断布局是不是空的 能不能解析 细节不用太关心
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
判断是不是有开始标签 没有抛异常
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
这句比较重要 获取根布局的名称 主要是后面判断根布局是不是merge
final String name = parser.getName();
判断根布局是不是merge标签 传进来的ViewGroup为空 或者attachToRoot为false的话抛异常
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(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate)
//解析布局并创建View 感兴趣的可以自己看一下
rInflate(parser, root, inflaterContext, attrs, false);
}
如果根布局不是merge
else {
//创建一个临时的View
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
//声明LayoutParams
ViewGroup.LayoutParams params = null;
//判断ViewGroup是否等于空
if (root != null) {
//通过generateLayoutParams(attrs)初始化LayoutParams
//方法内部其实是通过解析自定义属性一样 解析了attrs中的高度和宽度
//TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
//如果attachToRoot为false 就不会将创建的View(---->temp)添加到
//我们传进来的ViewGroup(---->root)中 只设置View 的LayoutParams
temp.setLayoutParams(params);
}
}
//初始化所有View(--->temp)下的所有子View 并将解析的子View添加到View(---->temp)中
rInflateChildren(parser, temp, attrs, true);
//=====================================================================
//如果传进来的ViewGroup不为空 并且 attachToRoot为true 就自动将View(--->temp)添加到
//我们传进来的ViewGroup(---->root)中
//=====================================================================
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//如果ViewGroup为空 或者 attachToRoot为空 将创建的View(--->temp)赋值给 result变量
if (root == null || !attachToRoot) {
result = temp;
}
}
//返回创建好的View对象
return result;