LayoutInflater.inflate我們經(jīng)常用于加載View就轧,比如:RecyclerView/ListView的item加載麻敌、Fragment.onCreateView怨酝、Dialog和PopupWindow setContextView等哑蔫,有些時(shí)候我們明明設(shè)置了寬高柄瑰,最后在顯示時(shí)卻沒有效果
案例
RecyclerView换吧,加載item
item布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:id="@+id/fill_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="填充的郵箱"
android:textSize="15sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
編譯器縮略圖應(yīng)該是:
實(shí)際顯示時(shí):
設(shè)置的高度沒有起到作用
原因
LayoutInflater.inflate未指定父布局導(dǎo)致LayoutParams缺失务嫡,也就是沒有指定父布局就沒有去讀取item頂層設(shè)置的寬高甲抖。可以使用LayoutInflater.inflate加載View時(shí)讓第二個(gè)參數(shù)為空心铃,然后getLayoutParams()這個(gè)時(shí)候獲取到的值為空
為什么會(huì)為空准谚?跟蹤哈LayoutInflater.inflate
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 {
//merge標(biāo)簽
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 {
// 加載view
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);
}
// 父布局去創(chuàng)建一個(gè)LayoutParams
params = root.generateLayoutParams(attrs);
//如果不把view添加到父view,就設(shè)置LayoutParams
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
//遞歸加載子View
rInflateChildren(parser, temp, attrs, true);
//父view不為空去扣,而且需要添加到父view
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//父view為空柱衔,或者不添加到父view,就把當(dāng)前加載view賦值給返回值
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
...
}
return result;
}
}
加載邏輯
- 無父View的情況下,直接加載View并返回,最后放回的是當(dāng)前加載的VIew
- 有父View的情況下唆铐,加載View后哲戚,調(diào)用父View的generateLayoutParams方法加載LayoutParams,如果不想把View添加到父View就設(shè)置LayoutParams艾岂,最后放回的是當(dāng)前加載的VIew
- 有父View并且要把加載視圖添加進(jìn)去,就會(huì)調(diào)用父View的addView方法顺少,最后返回的是父View
到這里就已經(jīng)理清楚為什么沒有設(shè)置父View,就會(huì)導(dǎo)致LayoutParams缺失澳盐。其實(shí)就是父View不明確的情況下祈纯,沒法去確定子View的屬性,所以這里的LayoutParams為空叼耙,那后續(xù)的加載不會(huì)出錯(cuò)腕窥?
沒有LayoutParams的Item,RecyclerView是如何去處理布局的
RecyclerView.tryGetViewHolderForPositionByDeadline
RecyclerView.ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
...
android.view.ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
RecyclerView.LayoutParams rvLayoutParams;
if (lp == null) {
rvLayoutParams = (RecyclerView.LayoutParams)RecyclerView.this.generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
}
...
這里如果沒有設(shè)置LayoutParams就使用LayoutManager.generateDefaultLayoutParams去創(chuàng)建一個(gè)默認(rèn)的LayoutParams筛婉,我們?cè)趚ml布局中設(shè)置的寬高就失效了簇爆。
如何處理?
1. 添加父View
- **在LayoutInflater.inflate時(shí)設(shè)置父View
- 在item布局中在添加一個(gè)父View爽撒,例如:上面的Item外層在添加ConstraintLayout
2. 添加LayoutParams
- 在LayoutInflater.inflate加載出View后入蛆,調(diào)用setLayoutParams
總結(jié)
控件 | 表述 |
---|---|
PopupWindow | 布局中xml root節(jié)點(diǎn)設(shè)置寬高失效, 如果創(chuàng)建時(shí)未傳入寬高硕勿,默認(rèn)寬高為0哨毁,不會(huì)顯示。寬高設(shè)置為自適應(yīng)時(shí)源武,會(huì)使用測量的寬高測量內(nèi)層view所占用的空間 |
RecyclerView | 布局中xml root節(jié)點(diǎn)設(shè)置寬高失效扼褪,設(shè)置父View或者設(shè)置LayoutParams或者在LayoutManager中重寫generateDefaultLayoutParams(不建議使用) |
Dialog | 布局中xml root節(jié)點(diǎn)設(shè)置寬高失效, setContentView時(shí)未傳入?yún)?shù)LayoutParams,者PhoneWindow.setContentView中會(huì)創(chuàng)建一個(gè)全屏的LayoutParams粱栖。傳入LayoutParams時(shí)使用傳入的參數(shù)作為root的LayoutParams话浇。 |
Fragment | 布局中xml root節(jié)點(diǎn)設(shè)置寬高失效, 他的LayoutParams受父布局影響,也就是FragmentTransaction add和replace 中IdRes對(duì)應(yīng)的控件闹究。使用父布局默認(rèn)的generateDefaultLayoutParams創(chuàng)建LayoutParams |