转载请注明出处 http://www.jianshu.com/p/3bf0d75d5ada (作者:韩栋)
由于本人水平有限,欢迎拍砖。
上文我们讲了
LayoutInflater
的使用方法。读者可以点击此LayoutInflater-使用
本文主要讲关于LayoutInflater源码分析,让读者更好地了解并使用它。
源码
直接切入正题。LayoutInflater.inflate()
源码如下:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
可以看到,它是一个重载方法。值得注意的是,root != null
这个逻辑,这也就为什么我在前文中说以下两个方法的实现是一模一样了。
View view =LayoutInflater.from(MainActivity.this).inflate(R.layout.view_child,frameLayout);
View view =LayoutInflater.from(MainActivity.this).inflate(R.layout.view_child,frameLayout,true);
来看inflate
具体实现:
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) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
这里只是根据我们传入的新布局文件获取到它的XmlResourceParser
对象。还是重载了inflate
方法。囧。。我们继续往下看inflate(parser, root, attachToRoot)
:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
//获取新的布局的属性集合
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
//省略···
final String name = parser.getName();
// 创建新的视图,注意:此时的新的视图的根布局的布局参数是空的
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
//省略···
if (root != null) {
// 这是重点:将原来新视图的根布局参数转换成适合`root`根布局的布局参数
params = root.generateLayoutParams(attrs);
//如果attachToRoot == false。则将新转换的布局参数赋给新视图
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
// 创建新视图下所有的子视图,忽略它,不是重点
rInflateChildren(parser, temp, attrs, true);
//如果root != null并且attachToRoot == true
if (root != null && attachToRoot) {
//将新视图将入到root中
root.addView(temp, params);
}
if (root == null || !attachToRoot) {
//将新的视图赋值给结果
result = temp;
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription()
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
return result;
}
}
嗯。这就是具体实现了。代码看过去貌似很长。其实很简单。只是毕竟是google大神
写的代码。考虑的东西非常全面。不过为了方便读者理解,我把一些非重要的代码给省略了。
废话不多说。这里主要说一下params = root.generateLayoutParams(attrs);
这个方法。这个方法调用的是root
中的generateLayoutParams
。这个方法是在ViewGroup
中定义的,用来根据已有的属性集合来转换成适合自身布局参数的的工具方法。我们简单看下FrameLayout
的重写generateLayoutParams
:
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new FrameLayout.LayoutParams(getContext(), attrs);
}
返回了一个LayoutParams
对象,继续看FrameLayout.LayoutParams
:
public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
super(c, attrs);
final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, UNSPECIFIED_GRAVITY);
a.recycle();
}
FrameLayout.LayoutParams
是FrameLayout
的一个内部类。它继承自MarginLayoutParams
。所以在这里调用了super(c, attrs)
,使这个LayoutParams对象支持Margin
的属性参数,以及在这里 gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, 。UNSPECIFIED_GRAVITY);
,获取gravity
属性(这可是FrameLayout
最重要的一个属性了。
言归正传。我们把新视图的根布局参数传递给root
。让root
进行转换成适合它自身布局的布局参数。
网友评论