1,获取LayoutInflater 对象
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;
}
2,LayoutInflater.inflate()方法参数
public View inflate(int resource, 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();
}
}
无论调用的是inflate的哪一种重载方法,最后调用的都是
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot),
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
//定义返回值,初始化为传入的形参root
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
//如果一开始就是END_DOCUMENT,那说明xml文件有问题
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
//有了上面判断说明这里type一定是START_TAG,也就是xml文件里的root node
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)) {
//处理merge tag的情况(merge,你懂的,APP的xml性能优化)
//root必须非空且attachToRoot为true,否则抛异常结束(APP使用merge时要注意的地方,
//因为merge的xml并不代表某个具体的view,只是将它包起来的其他xml的内容加到某个上层
//ViewGroup中。)
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//递归inflate方法调运
rInflate(parser, root, attrs, false, false);
} else {
// Temp is the root view that was found in the xml
//xml文件中的root view,根据tag节点创建view对象
final View temp = createViewFromTag(root, name, attrs, false);
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
//根据root生成合适的LayoutParams实例
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//如果attachToRoot=false就调用view的setLayoutParams方法
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp
//递归inflate剩下的children
rInflate(parser, temp, attrs, true, 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非空且attachToRoot=true则将xml文件的root view加到形参提供的root里
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) {
//返回xml里解析的root view
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;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
//返回参数root或xml文件里的root view
return result;
}
}
当我们infalte布局之后会调用View类的
onFinishInflate回调(和setContentView的回调
onContentChanged
类似),所以,可以看见,当我们自定义View时在构造函数inflate一个xml后可以实现onFinishInflate这个方法一些自定义的逻辑。
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
*
* <p>Even if the subclass overrides onFinishInflate, they should always be
* sure to call the super method, so that we get called.
*/
protected void onFinishInflate() {
}
- inflate(xmlId, null); 只创建temp的View,然后直接返回temp,xml中最外层布局的 宽高 失效。
- inflate(xmlId, parent); 创建temp的View,然后执行root.addView(temp, params);最后返回root。xml中最外层布局的 宽高 有效。
- inflate(xmlId, parent, false); 创建temp的View,然后执行temp.setLayoutParams(params);然后再返回temp。
- inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp, params);最后返回root。
- inflate(xmlId, null, false); 只创建temp的View,然后直接返回temp。
- inflate(xmlId, null, true); 只创建temp的View,然后直接返回temp。
我们通常在xml布局文件中,使用layout_width和layout_height来设置View的大小,但其实这不是view的真是大小,实际设置的是view在ViewGroup布局中的大小,所以
反过来看命名,是layout_width,而不是width。
但是,问题来了,我们在setContentView的时候,也是通过inflate注入布局的,那时候xml最外层的布局指定大小都是有效的啊,那是为什么呢?
因为,xml文件布局是放在id为content的Fragment中的,所以layout_width和layout_height才会生效。
3,inflate(xmlId, parent, true); 创建temp的View,然后执行root.addView(temp, params);最后返回root。layout_width和layout_height的真实含义
例子:listView中的item通过inflate的方式,注入布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listview"
android:dividerHeight="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
textview_layout.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Text Test"
android:background="#ffa0a00c"/>
textview_layout_parent.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Text Test"
android:background="#ffa0a00c"/>
</LinearLayout>
ListView的自定义Adapter文件:
public class InflateAdapter extends BaseAdapter {
private LayoutInflater mInflater = null;
public InflateAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 8;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//说明:这里是测试inflate方法参数代码,不再考虑性能优化等TAG处理
return getXmlToView(convertView, position, parent);
}
private View getXmlToView(View convertView, int position, ViewGroup parent) {
View[] viewList = {
mInflater.inflate(R.layout.textview_layout, null),
// mInflater.inflate(R.layout.textview_layout, parent),
mInflater.inflate(R.layout.textview_layout, parent, false),
// mInflater.inflate(R.layout.textview_layout, parent, true),
mInflater.inflate(R.layout.textview_layout, null, true),
mInflater.inflate(R.layout.textview_layout, null, false),
mInflater.inflate(R.layout.textview_layout_parent, null),
// mInflater.inflate(R.layout.textview_layout_parent, parent),
mInflater.inflate(R.layout.textview_layout_parent, parent, false),
// mInflater.inflate(R.layout.textview_layout_parent, parent, true),
mInflater.inflate(R.layout.textview_layout_parent, null, true),
mInflater.inflate(R.layout.textview_layout_parent, null, false),
};
convertView = viewList[position];
return convertView;
}
}
效果图:
mInflater.inflate(R.layout.textview_layout, null)
不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null。
mInflater.inflate(R.layout.textview_layout, parent)
能正确显示我们设置的宽高是因为我们的View在设置setLayoutParams时params = root.generateLayoutParams(attrs)
不为空。Inflate(resId , parent,false ) 可以正确处理,因为temp.setLayoutParams(params);这个params正是root.generateLayoutParams(attrs);得到的
mInflater.inflate(R.layout.textview_layout, null, true)与mInflater.inflate(R.layout.textview_layout, null, false)
不能正确处理我们设置的宽和高是因为layout_width,layout_height是相对了父级设置的,而此temp的getLayoutParams为null
- textview_layout_parent.xml作为item可以正确显示的原因是因为TextView具备上级ViewGroup,上级ViewGroup的layout_width,layout_height会失效,当前的TextView会有效而已。