在ViewGroup的默认addView 方法中, 如果发现 v 没有layout参数,则生成一个默认的layoutParams, 这个参数的layout默认是wrap_content的, 所以有时候我们在xml顶层元素写的 match_parent不生效, 原因是 LayoutInflater.inflate(int res, ViewGroup root, boolean attachToRoot) 的第二个参数root我们设置的是空, 这时候xml顶层元素设置的layout参数将统统忽略, 要想让这些参数生成v的layoutparams, 需要提供合适的 root, 因为不同的ViewGroup子类支持的layout参数是不同的。
/**
* Adds a child view. If no layout parameters are already set on the child, the
* default parameters for this ViewGroup are set on the child.
*
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
*
* @param child the child view to add
* @param index the position at which to add the child
*
* @see #generateDefaultLayoutParams()
*/
public void addView(View child, int index) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
...
/**
* Returns a set of default layout parameters. These parameters are requested
* when the View passed to {@link #addView(View)} has no layout parameters
* already set. If null is returned, an exception is thrown from addView.
*
* @return a set of default layout parameters or null
*/
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
比如一个继承了LinearLayout的MyLinearLayout的初始化init方法中,生成test_layout时传入的root参数是null, 那么test_layout生成的view其实并不能填充其parent,只需将null改为this就可以解决这个问题:
View v = mLayoutInflater.inflate(R.layout.test_layout, null, false);
addView(v);
View v = mLayoutInflater.inflate(R.layout.test_layout, this, false);
addView(v);
// test_layot.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:apad="http://schemas.android.com/apk/res/com.taobao.apad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/red">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginTop="20dp"
....
</LinearLayout>
从下面的inflate过程可以明显的看出这一逻辑, 值得一提的是其中对 blink 标签的处理,会生成一个有闪烁效果的view ==, BlinkLayout 是LayoutInflater的内部类,每个一个时间(500)会invalidate一次。
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;
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 (TAG_MERGE.equals(name)) {
// deal with merge tag
} else {
// Temp is the root view that was found in the xml
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
ViewGroup.LayoutParams params = null;
// if root is not null, the created view will have a non-null layout param
if (root != null) {
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 (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) {
...
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
像这样就可以生成一个闪烁的text view :
<blink xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Blinking text" />
</blink>
另外,在inflate 时如果提供了 containerView, 那么被inflate的view 会执行一次 onLayout, 如果没有, 那么只有等到view被 addView 进一个parentView后才能执行layout。