【Android開發經驗】LayoutInflater——80%的Android程式設計師對它並不瞭解甚至錯誤使用
這個標題起的有點誇張哈,但是LayoutInflater這個類的一些用法,在Android開發者使用的過程中,確實存在著一些很普遍的誤區,最起碼我研究的這麼多小專案的原始碼,基本上都在錯誤的使用這個類。今天,看到了一篇文章講LayoutInflater的用法,瞬間感覺自己對這個類確實不夠了解,於是簡單的看了下LayoutInflater類的原始碼,對這個類有了新的認識。
首先,LayoutInflater這個類是用來幹嘛的呢?
我們最常用的便是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)
這四種使用方式中,我們最常用的是第一種方式,inflate方法的主要作用就是將xml轉換成一個View物件,用於動態的建立佈局。雖然過載了四個方法,但是這四種方法最終呼叫的,還是第四種方式。第四種方式也很好理解,內部實現原理就是利用Pull解析器,對Xml檔案進行解析,然後返回View物件。
我們以我們經常使用的第一種形式為例,你在重寫BaseAdapter的getView方法的時候是否這樣做過
[java] view plaincopyprint?- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = inflate(R.layout.item_row, null);
- }
- return convertView;
- }
inflate方法有三個引數,分別是public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflate(R.layout.item_row, null); } return convertView; }
1.resource佈局的資源id
2.root填充的根檢視
3.attachToRoot是否將載入的檢視繫結到根檢視中
在這個例子中,我們將root引數設為空,功能確實實現了,但是這裡還隱藏著一個隱患,這種方式並不是inflate正確的使用姿勢,下面我們通過一個Demo,來說一下這樣使用造成的弊端。
首先,我們建立一個這樣的專案
這裡三個介面,一個主介面,兩個測試介面,佈局檔案中,主介面只負責介面跳轉,兩個測試介面都是一個簡單的Listview,item佈局顯示效果如下
對應的佈局檔案如下
[html] view plaincopyprint?- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:background="@android:color/holo_orange_light"
- android:gravity="center"
- android:orientation="vertical">
- <TextView
- android:id="@+id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="11"
- android:textColor="@android:color/black"
- android:textSize="22sp"/>
- </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="11"
android:textColor="@android:color/black"
android:textSize="22sp" />
</LinearLayout>
OneActivity的程式碼如下 [java] view plaincopyprint?
- publicclass OneActivity extends Activity {
- private ListView list1;
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_one);
- list1 = (ListView) findViewById(R.id.list1);
- list1.setAdapter(new MyAdapter(this));
- }
- privateclass MyAdapter extends BaseAdapter {
- private LayoutInflater inflater;
- MyAdapter(Context context) {
- inflater = LayoutInflater.from(context);
- }
- @Override
- publicint getCount() {
- return 20;
- }
- @Override
- public Object getItem(int position) {
- return position;
- }
- @Override
- publiclong getItemId(int position) {
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = inflater.inflate(R.layout.item_list, null);
- }
- TextView tv = (TextView) convertView.findViewById(R.id.tv);
- tv.setText(position+"");
- return convertView;
- }
- }
- }
public class OneActivity extends Activity {
private ListView list1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
list1 = (ListView) findViewById(R.id.list1);
list1.setAdapter(new MyAdapter(this));
}
private class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
MyAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 20;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_list, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tv);
tv.setText(position+"");
return convertView;
}
}
}
TwoActivity的程式碼如下 [java] view plaincopyprint?
- publicclass TwoActivity extends Activity {
- private ListView list2;
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_two);
- list2 = (ListView) findViewById(R.id.list2);
- list2.setAdapter(new MyAdapter(this));
- }
- privateclass MyAdapter extends BaseAdapter {
- private LayoutInflater inflater;
- MyAdapter(Context context) {
- inflater = LayoutInflater.from(context);
- }
- @Override
- publicint getCount() {
- return20;
- }
- @Override
- public Object getItem(int position) {
- return position;
- }
- @Override
- publiclong getItemId(int position) {
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = inflater.inflate(R.layout.item_list, parent,false);
- }
- TextView tv = (TextView) convertView.findViewById(R.id.tv);
- tv.setText(position + "");
- return convertView;
- }
- }
- }
public class TwoActivity extends Activity {
private ListView list2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
list2 = (ListView) findViewById(R.id.list2);
list2.setAdapter(new MyAdapter(this));
}
private class MyAdapter extends BaseAdapter {
private LayoutInflater inflater;
MyAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return 20;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_list, parent,false);
}
TextView tv = (TextView) convertView.findViewById(R.id.tv);
tv.setText(position + "");
return convertView;
}
}
}
兩個檔案最關鍵的區別就一句話,
在getView方法中,OneActivity是
convertView = inflater.inflate(R.layout.item_list, null);
在getView方法中,TwoActivity是
convertView = inflater.inflate(R.layout.item_list, parent,false);
我們先看一下顯示效果,再說兩者的區別
OneActivity效果
TwoActivity的顯示效果
我們可以很明顯的看出來,使用第一種方式,根佈局的高度設定60dp沒有起作用,系統還是按照包裹內容的方式載入的,為什麼會產生這種效果呢?我們從需要inflate方法的原始碼中找一下答案。
首先,方式一的原始碼實現
[java] view plaincopyprint?- public View inflate(XmlPullParser parser, ViewGroup root) {
- return inflate(parser, root, root != null);
- }
public View inflate(XmlPullParser parser, ViewGroup root) {
return inflate(parser, root, root != null);
}
當我們使用方式一,並且第二個引數傳入null的時候,預設呼叫的是下面的方法,並且attachToRoot是false [java] view plaincopyprint?
- 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(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();
}
}
在這一個方法中,pull解析器將資源id轉化成XmlResourceParser物件,又傳給了第四種方式,所以我們需要重點看的還是第四種方式是如何實現的 [java] view plaincopyprint?
- 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) {
- thrownew 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 {
- // 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 != 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
- rInflate(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) {
- 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);
- return result;
- }
- }
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 (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 {
// 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 != 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
rInflate(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) {
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);
return result;
}
}
程式碼比較長,我們重點關注下面的程式碼 [java] view plaincopyprint?
- 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 (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);
}
}
這些程式碼的意思就是,當我們傳進來的root引數不是空的時候,並且attachToRoot是false的時候,也就是上面的TwoActivity的實現方式的時候,會給temp設定一個LayoutParams引數。那麼這個temp又是幹嘛的呢? [java] view plaincopyprint?
- <pre name="code"class="java">// 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;
- }
<pre name="code" class="java">// 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;
}
現在應該明白了吧,當我們傳進來的root不是null,並且第三個引數是false的時候,這個temp就被加入到了root中,並且把root當作最終的返回值返回了。而當我們設定root為空的時候,沒有設定LayoutParams引數的temp物件,作為返回值返回了。
因此,我們可以得出下面的結論:
1.若我們採用convertView = inflater.inflate(R.layout.item_list, null);方式填充檢視,item佈局中的根檢視的layout_XX屬性會被忽略掉,然後設定成預設的包裹內容方式
2.如果我們想保證item的檢視中的引數不被改變,我們需要使用convertView = inflater.inflate(R.layout.item_list, parent,false);這種方式進行檢視的填充
3.除了使用這種方式,我們還可以設定item佈局的根檢視為包裹內容,然後設定內部控制元件的高度等屬性,這樣就不會修改顯示方式了。
最後,給出那篇文章的連結http://blog.jobbole.com/72156/大家可以去看看
相關推薦
【Android開發經驗】LayoutInflater——80%的Android程式設計師對它並不瞭解甚至錯誤使用
這個標題起的有點誇張哈,但是LayoutInflater這個類的一些用法,在Android開發者使用的過程中,確實存在著一些很普遍的誤區,最起碼我研究的這麼多小專案的原始碼,基本上都在錯誤的使用這個類。今天,看到了一篇文章講LayoutInflater的用法,瞬間感覺自己對
【Android開發經驗】android:windowSoftInputMode屬性具體解釋
中一 mtk wid water ren 而是 orien 表示 不能 本文章來自CSDN博客:http://blog.csdn.net/zhaokaiqiang1992。轉載請註明地址! 在前面的一篇文章中,簡單的介紹了一下怎樣實現軟鍵盤不自己主動彈出,使
【Android開發經驗】APP的快取檔案到底應該存在哪?看完這篇文章你應該就自己清楚了
只要是需要進行聯網獲取資料的APP,那麼不管是版本更新,還是圖片快取,都會在本地產生快取檔案。那麼,這些快取檔案到底放在什地方合適呢?系統有沒有給我們提供建議的快取位置呢?不同的快取位置有什麼不同呢?今天這篇文章就是主要來說明這個問題的。 首先,我們要知道,在A
【Android開發經驗】設定使用者頭像並裁剪,僅僅是這麼簡單?
在做APP的時候,如果有使用者系統功能,那麼一般都逃不了這個需求,就是給使用者設定頭像,而設定頭像,又包括從拍照和從相簿選取兩個方式,而且選擇了之後,一般又都會要求對影象進行裁剪,讓使用者設定頭像。今天這篇文章就是介紹如何完成這個需求的。 我們首先分析一下需求。
【Android開發經驗】關於“多執行緒斷點續傳下載”功能的一個簡單實現和講解
上班第一天,在技術群裡面和大家閒扯,無意中談到了關於框架的使用,一個同學說為了用xUtils的斷線續傳下載功能,把整個庫引入到了專案中,在google的官方建議中,是非常不建議這種做法的,集合框架雖然把很多功能整合起來,但是程式碼越多,出現問題的可能越大,而且無形之中
【劍指offer】陣列中的逆序對(校正書上錯誤)【歸併排序】
題目描述 在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007 題目保證輸入的陣列中沒有的相同的數字 資料範圍:
分享8年開發經驗,淺談java程式設計師職業規劃
在中國有很多人都認為IT行為是吃青春飯的,如果過了30歲就很難有機會再發展下去!其實現實並不是這樣子的,在下從事.NET及JAVA方面的開發的也有8年的時間了,在這裡在下想憑藉自己的親身經歷,與大家一起探討一下。 明確入行的目的 很多人幹IT這一行都衝著“收入高”這一點的,
【Android開發-5】界面裝修,五大布局你選誰
比例 技術分享 article 嵌套 content java lin layout mark 前言:假設要開一家店,門店裝修是非常重要的事情。有錢都請專門的建築設計公司來設計裝修,沒錢的僅僅能自己瞎折騰。好不好看全憑自己的感覺。像Android開發。在移動端大家看到的
【android開發筆記】如何讓ImageButton去掉白色邊框和讓ImageButton具有點選效果
這是我從網上學來的,怕忘記,遂記起來 如何讓ImageButton去掉白色邊框 android:background="#00000000" //把背景變透明放在那一段程式碼裡呢?放在layout裡面的.xml檔案裡 如: <ImageButton a
【Linux/Android開發記錄】學習、記錄、分享Linux/Android開發技術
專欄達人 授予成功建立個人部落格專欄
【Android開發日記】Popupwindow 完美demo
Popupwindow 完美demo實現 圖示: 關鍵程式碼說明:
【Android 開發入門】android studio 控制檯列印輸出日誌
有些情況下,不方便使用斷點的方式來除錯,而是希望在控制檯列印輸出日誌,使用過Eclipse的同學都知道Java可以使用 System.out.println(""); 來在控制檯列印輸出日誌,但是在android studio中卻是不行的,還是有差別的,那應該用什麼呢?and
【Android開發bug】Dropping event due to no window focus
問題描述: 使用android自帶視訊播放器播放視訊,按返回鍵不能退出這個介面(Android_4.4.4系統); Log如下: 11-25 16:37:31.498: W/ViewRootImpl(27708): Dropping event due to no win
【Android 開發入門】使用Android模擬器預覽除錯程式
在上篇“走進Android開發的世界,HelloWorld”,我們建立了一個Android 專案 HelloWorld,並演示瞭如何通過USB連線手機檢視執行效果;而如果沒有手機或沒有對應型號的手機,又
【Android 開發入門】我為什麼要在Android找工作越來越難的時候開始學習它
首先,Android是不是真的找工作越來越難呢?這個可能是大家最關心的。這個受大的經濟環境以及行業發展前景的影響,同時也和個人因素有關。2016-08-26近期一方面是所在的公司招聘Java開發人員很難招到合適的,投簡歷的人很少;而另一方面,經常聽身邊的人說Android、i
【Android開發技巧】Fragment中獲取Context物件的兩種方法
我們在使用Fragment的時候經常需要傳遞Context型別的引數,【Android用的比較多的就是下面幾個導航按鈕,點選切換fragment(之前Android studio2.0以前是可以new fragment(context)時傳遞Context,但後來升級了studio2.1.2之後,new
【Android 開發教程】使用Intent傳送簡訊
本章節翻譯自《Beginning-Android-4-Application-Development》,如有翻譯不當的地方,敬請指出。使用SmsManager類,可以在自己編寫的程式內部發送簡訊,而不需要呼叫系統的簡訊應用。然而,有的時候呼叫系統內建的簡訊應用會更加方便。這時
【android實戰經驗】關閉Fragment的方法
getActivity().onBackPressed()。 該方法用於監聽使用者點選返回鍵的事件,也可以呼叫它來關閉view。 在FragmentTransaction物件中新增fragment
【商城開發三】Android 仿淘寶商品詳情頁下拉足跡修改版
開發商城的快有半個月了,需要做到詳情頁下拉足跡的效果,網上找了找沒找到,找到一個差不多還有點問題,然後在基礎上進行了二次開發 感謝http://blog.csdn.net/yaphetzhao/article/details/53736471 YaphetZhao的部落格
【Android 開發入門】Android裝置監視器之除錯工具DDMS使用初探
Android Studio提供了一個很實用的工具Android裝置監視器(Android device monitor),該監視器中最常用的一個工具就是DDMS(Dalvik Debug Monitor Service),是 Android 開發環境中的Dalvik虛擬機器