之前用RecyclerView爲了達到自己想要的結果,把item的根佈局(最外層Layout)的大小設爲match_parent,一開始卻發現一個很大的問題!咦?爲什麼我的item一加載就成了wrap_content的效果?我的match_parent爲什麼效果顯示不出來…在嘗試了很多很多方法覺得應該不是我寫錯了之後,我才意識到我根本不知道LayoutInflater的inflate這個函數的參數的意義,查了api還是不解,這個第三個參數attachToRoot到底是啥意思?爲了弄懂這個問題,看了很多博客,覺得這個是個好問題!弄懂了它,你再也不會錯誤的用inflate了!
我們最常用的便是LayoutInflater的inflate方法,這個方法重載了四種調用方式,分別爲:
1. public View inflate(int resource, ViewGroup root)
2. public View inflate(int resource, ViewGroup root, boolean attachToRoot)
3.public View inflate(XmlPullParser parser, ViewGroup root)
4.public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
而我們最常用的用法就是這樣(RecyclerView的Adapter中):
-
@Override
-
public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-
View view = mInflater.inflate(R.layout.item_agenda_detail, null);
-
AgendaDetailHolder holder = new AgendaDetailHolder(view);
-
return holder;
-
}
看似很簡單的一個調用,原來有四個重載,而我們最簡單的用法就是上面這段用法,也是我一開始的用法(複製粘貼就是這樣,你也不會去看細節)
可當你運行測試的時候,你驚訝的發現,說好的效果呢?
下面是我item的佈局文件,清清楚楚的寫着match_parent
-
<?xml version="1.0" encoding="utf-8"?>
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
android:orientation="vertical"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<View
-
android:layout_width="match_parent"
-
android:layout_height="50dp">
-
</View>
-
<LinearLayout
-
android:orientation="vertical"
-
android:id="@+id/id_agenda_detail_layout"
-
android:background="#afbfff"
-
android:layout_width="match_parent"
-
android:layout_height="0dp"
-
android:layout_weight="1">
-
<LinearLayout
-
android:background="#FFFFFF"
-
android:orientation="vertical"
-
android:layout_marginLeft="2dp"
-
android:layout_marginRight="2dp"
-
android:layout_marginTop="2dp"
-
android:layout_marginBottom="2dp"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<TextView
-
android:id="@+id/id_text_view_agenda_title"
-
android:gravity="center"
-
android:textSize="30sp"
-
android:text="議程標題"
-
android:layout_width="match_parent"
-
android:layout_height="0dp"
-
android:layout_weight="1"/>
-
<TextView
-
android:id="@+id/id_text_view_agenda_speaker"
-
android:gravity="center"
-
android:textSize="20sp"
-
android:text="主講人:"
-
android:layout_width="match_parent"
-
android:layout_height="0dp"
-
android:layout_weight="1"/>
-
<TextView
-
android:id="@+id/id_text_view_agenda_time"
-
android:gravity="center"
-
android:textSize="20sp"
-
android:text="議程時長:5:00"
-
android:layout_width="match_parent"
-
android:layout_height="0dp"
-
android:layout_weight="1"/>
-
<RelativeLayout
-
android:layout_width="match_parent"
-
android:layout_height="0dp"
-
android:layout_weight="1">
-
-
<Button
-
android:id="@+id/id_btn_show_agenda_file"
-
android:text="查看文件"
-
android:layout_centerInParent="true"
-
android:layout_width="100dp"
-
android:layout_height="50dp" />
-
-
</RelativeLayout>
-
-
</LinearLayout>
-
</LinearLayout>
-
<View
-
android:layout_width="match_parent"
-
android:layout_height="50dp">
-
</View>
-
-
</LinearLayout>
運行的效果我就不說了…擠在一起的item,醜陋不堪,完全和我預想的效果不一樣!然後我試了一下wrap_content,我驚訝的發現,真的一模一樣……所以說我的match_parent竟然被改成了wrap_content!
網上查了一圈,我發現我的adapter代碼和別人的不一樣!於是,我機智地改成了這樣:
-
@Override
-
public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-
View view = mInflater.inflate(R.layout.item_agenda_detail, parent, false);
-
AgendaDetailHolder holder = new AgendaDetailHolder(view);
-
return holder;
-
}
運行了一遍之後,我發現竟然可以了!我的效果又設置成功了,match_parent又有了效果!
但我強烈地想知道,到底爲什麼有了效果…所以我開始了查找資料的過程,源碼中是這麼寫的
-
public View inflate(int resource, ViewGroup root) {
-
return inflate(resource, root, root != null);
-
}
-
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
-
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
-
XmlResourceParser parser = getContext().getResources().getLayout(resource);
-
try {
-
return inflate(parser, root, attachToRoot);
-
} finally {
-
parser.close();
-
}
-
}
這是前兩種的調用,它們最終都會調用第四個重載,所以我們關注這個即可
-
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 {
-
-
int type;
-
while ((type = parser.next()) != XmlPullParser.START_TAG &&
-
type != XmlPullParser.END_DOCUMENT) {
-
-
}
-
-
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, attrs, false);
-
} else {
-
-
View temp;
-
if (TAG_1995.equals(name)) {
-
temp = new BlinkLayout(mContext, attrs);
-
} else {
-
temp = createViewFromTag(root, name, attrs);
-
}
-
-
ViewGroup.LayoutParams params = null;
-
-
if (root != null) {
-
if (DEBUG) {
-
System.out.println("Creating params from root: " +
-
root);
-
}
-
-
params = root.generateLayoutParams(attrs);
-
if (!attachToRoot) {
-
-
-
temp.setLayoutParams(params);
-
}
-
}
-
-
if (DEBUG) {
-
System.out.println("-----> start inflating children");
-
}
-
-
rInflate(parser, temp, attrs, true);
-
if (DEBUG) {
-
System.out.println("-----> done inflating children");
-
}
-
-
-
-
if (root != null && attachToRoot) {
-
root.addView(temp, params);
-
}
-
-
-
-
if (root == null || !attachToRoot) {
-
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 {
-
-
mConstructorArgs[0] = lastContext;
-
mConstructorArgs[1] = null;
-
}
-
-
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-
-
return result;
-
}
-
}
代碼很長,關鍵是這一段
-
ViewGroup.LayoutParams params = null;
-
-
if (root != null) {
-
if (DEBUG) {
-
System.out.println("Creating params from root: " +
-
root);
-
}
-
-
params = root.generateLayoutParams(attrs);
-
if (!attachToRoot) {
-
-
-
temp.setLayoutParams(params);
-
}
-
}
我們的root終於出現了,意思就是當root不是空的時候,並且你的attachToRoot位false,就把root的參數設置給temp,那root的實參parent是啥呢?在這個
-
<pre name="code" class="java">public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType)
函數中,parent就是RecyclerView,即你item的父試圖,你創建的佈局的即我們設置的layout的參數的計算都要依賴於這個父視圖,而沒有這個父視圖(null)等於告訴框架你不需要父視圖去添加你的view
那這個temp是幹嘛的,我們接着看代碼
-
-
-
if (root == null || !attachToRoot) {
-
result = temp;
-
}
而這個result最終就是我們的返回結果,那我們的參數就成功的得到了計算(因爲有了父視圖,match_parent被計算了大小,就是你父視圖,即你的RecyclerView的大小),那我想要的效果就得到設置!
通過這一行行的解釋,我們終於弄明白了!root的作用就是來計算我們item的設置參數,沒有root自然也沒辦法計算參數,那我們設置什麼都不會有效果!可能我的理解會有一點偏差,不過起碼也算知道了我們的設置爲什麼沒有用的原因,後幾篇文章我想對LayoutInflater做一個根本的解析,真正理解這個大師的原理。