研究这个问题,起源于在学习RecyclerView
时,在onCreateViewHolder
中:
@Override
public UseAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view1 = View.inflate(parent.getContext(), R.layout.use_item, null);
// View view2 = LayoutInflater.from(parent.getContext())
// .inflate(R.layout.use_item, parent, false);
return new ViewHolder(view);
}
view1和view2的效果不一致,故而研究这个问题。
View.inflate(…)
源码:
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
内部也是用LayoutInflater实现的。
LayoutInflater.from(getActivity()).inflate(…)
源码:
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
只不过是构建了LayoutInflater 。
inflater.inflate(…)
//方法一:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
//方法二:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
//方法三:
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();
}
}
//方法四:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// 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!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
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, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
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;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
方法一:传的是resource的id,调用方法三
方法二:传的是resource解析之后的XmlPullParser,调用方法四
方法三:根据id,将resource解析成XmlPullParser,调用方法四
方法四:view的解析
简单来说,就是:
attachToRoot
决定是否将view添加至root
root
是否为空,则决定能否获取view的布局参数
再回到开始的问题:
View view1 = View.inflate(parent.getContext(), R.layout.use_item, parent);
其实相当于
View view2 = LayoutInflater.from(parent.getContext()).inflate(R.layout.use_item, parent, true);
既获取了布局参数,又添加至父布局,如果:
View view1 = View.inflate(parent.getContext(), R.layout.use_item, null);
那么,无法获取到布局参数,因此布局全部靠左。
正确的使用方法是:
// View view2 = LayoutInflater.from(parent.getContext())
// .inflate(R.layout.use_item, parent, false);
既获取了布局参数,同时又不添加至父布局。
为啥在viewholder中不能添加至父布局呢?
因为viewholder将view包装一层,再添加至parent,如果此时添加,就会添加两次。
报错:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
三个案例带你看懂LayoutInflater中inflate方法两个参数和三个参数的区别 - 江南一点雨的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/u012702547/article/details/52628453
关于inflate的第3个参数 - Yuxing - 博客园
http://www.cnblogs.com/yuxing/archive/2012/02/18/2357740.html
Android LayoutInflate深度解析 给你带来全新的认识 - 推酷
http://www.tuicool.com/articles/MJFNBfF
View.inflate和LayoutInflater的inflate方法区别
http://m.blog.csdn.net/article/details?id=51055283
Android基础:三种inflate的区别 - AndroidCQC的博客 - 博客频道 - CSDN.NET
http://blog.csdn.net/ss1168805219/article/details/52047528
Android LayoutInflater原理分析,带你一步步深入了解View(一) - 郭霖的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/guolin_blog/article/details/12921889