Android四大組件
1.ContentProvider
共享應用程序內的數據,在數據修改時可以監聽
1.Activity
供用戶操作的界面
2.BroadcastReceiver
用來接收廣播,可以根據系統發生的一些時間做出一些處理
3.Service
長期在后臺運行的,沒有界面的組件,用來在后臺執行一些耗時的操作
?熟悉掌握ListView的優化及異步任務加載網絡數據
一、異步任務加載網絡數據:
在Android中提供了一個異步任務的類AsyncTask,簡單來說,這個類中的任務是運行在后臺線程中的,并可以將結果放到UI線程中進行處理,它定義了三種泛型,分別是Params、Progress和Result,分別表示請求的參數、任務的進度和獲得的結果數據。
1、使用原因:
1)是其中使用了線程池技術,而且其中的方法很容易實現調用
2)可以調用相關的方法,在開啟子線程前和后,進行界面的更新
3)一旦任務多了,不用每次都new新的線程,可以直接使用
2、執行的順序:
onPreExecute()【執行前開啟】--- > doInBackground() --- > onProgressUpdate() --- >onPostExecute()
3、執行過程:
當一個異步任務開啟后,執行過程如下:
1)、onPreExecute():
這個方法是執行在主線程中的。這步操作是用于準備好任務的,作為任務加載的準備工作。建議在這個方法中彈出一個提示框。
2)、doInBackground():
這個方法是執行在子線程中的。在onPreExecute()執行完后,會立即開啟這個方法,在方法中可以執行耗時的操作。需要將請求的參數傳遞進來,發送給服務器,并將獲取到的數據返回,數據會傳給最后一步中;這些值都將被放到主線程中,也可以不斷的傳遞給下一步的onProgressUpdate()中進行更新。可以通過不斷調用publishProgress(),將數據(或進度)不斷傳遞給onProgressUpdate()方法,進行不斷更新界面。
3)、onProgressUpdate():
這個方法是執行在主線程中的。publishProgress()在doInBackground()中被調用后,才開啟的這個方法,它在何時被開啟是不確定的,執行這個方法的過程中,doInBackground()是仍在執行的,即子線程還在運行著。
4)、onPostExecute():
這個方法是執行在主線程中的。當后臺的子線程執行完畢后才調用此方法。doInBackground()返回的結果會作為參數被傳遞過來。可以在這個方法中進行更新界面的操作。
5)、execute():
最后創建AsyncTask自定義的類,開啟異步任務。
3、實現原理:
1)、線程池的創建:
在創建了AsyncTask的時候,會默認創建一個線程池ThreadPoolExecutor,并默認創建出5個線程放入到線程池中,最多可防128個線程;且這個線程池是公共的唯一一份。
2)、任務的執行:
在execute中,會執行run方法,當執行完run方法后,會調用scheduleNext()不斷的從雙端隊列中輪詢,獲取下一個任務并繼續放到一個子線程中執行,直到異步任務執行完畢。
3)、消息的處理:
在執行完onPreExecute()方法之后,執行了doInBackground()方法,然后就不斷的發送請求獲取數據;在這個AsyncTask中維護了一個InternalHandler的類,這個類是繼承Handler的,獲取的數據是通過handler進行處理和發送的。在其handleMessage方法中,將消息傳遞給onProgressUpdate()進行進度的更新,也就可以將結果發送到主線程中,進行界面的更新了。
4、需要注意的是:
①、這個AsyncTask類必須由子類調用
②、雖然是放在子線程中執行的操作,但是不建議做特別耗時的操作,如果操作過于耗時,建議使用線程池ThreadPoolExecutor和FutureTask
示例代碼:
二、ListView優化:
ListView的工作原理
首先來了解一下ListView的工作原理(可參見http://mobile.51cto.com/abased-410889.htm),如圖:
1、如果你有幾千幾萬甚至更多的選項(item)時,其中只有可見的項目存在內存(內存內存哦,說的優化就是說在內存中的優化!!!)中,其他的在Recycler中
2、ListView先請求一個type1視圖(getView)然后請求其他可見的項目。convertView在getView中是空(null)的
3、當item1滾出屏幕,并且一個新的項目從屏幕低端上來時,ListView再請求一個type1視圖。convertView此時不是空值了,它的值是item1。你只需設定新的數據然后返回convertView,不必重新創建一個視圖
一、復用convertView
,減少findViewById
的次數
1、優化一:復用convertView
Android系統本身為我們考慮了ListView的優化問題,在復寫的Adapter的類中,比較重要的兩個方法是getCount()和getView()。界面上有多少個條顯示,就會調用多少次的getView()方法;因此如果在每次調用的時候,如果不進行優化,每次都會使用View.inflate(….)的方法,都要將xml文件解析,并顯示到界面上,這是非常消耗資源的:因為有新的內容產生就會有舊的內容銷毀,所以,可以復用舊的內容。
優化:
在getView()方法中,系統就為我們提供了一個復用view的歷史緩存對象convertView,當顯示第一屏的時候,每一個item都會新創建一個view對象,這些view都是可以被復用的;如果每次顯示一個view都要創建一個,是非常耗費內存的;所以為了節約內存,可以在convertView不為null的時候,對其進行復用
2、優化二:緩存item條目的引用——ViewHolder
findViewById()這個方法是比較耗性能的操作,因為這個方法要找到指定的布局文件,進行不斷地解析每個節點:從最頂端的節點進行一層一層的解析查詢,找到后在一層一層的返回,如果在左邊沒找到,就會接著解析右邊,并進行相應的查詢,直到找到位置(如圖)。因此可以對findViewById進行優化處理,需要注意的是:
》》》》特點:xml文件被解析的時候,只要被創建出來了,其孩子的id就不會改變了。根據這個特點,可以將孩子id存入到指定的集合中,每次就可以直接取出集合中對應的元素就可以了。
優化:
在創建view對象的時候,減少布局文件轉化成view對象的次數;即在創建view對象的時候,把所有孩子全部找到,并把孩子的引用給存起來
①定義存儲控件引用的類ViewHolder
這里的ViewHolder類需要不需要定義成static,根據實際情況而定,如果item不是很多的話,可以使用,這樣在初始化的時候,只加載一次,可以稍微得到一些優化
不過,如果item過多的話,建議不要使用。因為static是Java中的一個關鍵字,當用它來修飾成員變量時,那么該變量就屬于該類,而不是該類的實例。所以用static修飾的變量,它的生命周期是很長的,如果用它來引用一些資源耗費過多的實例(比如Context的情況最多),這時就要盡量避免使用了。
classViewHolder{
//定義item中相應的控件
}
②創建自定義的類:ViewHolderholder = null;
③將子view添加到holder中:
在創建新的listView的時候,創建新的ViewHolder,把所有孩子全部找到,并把孩子的引用給存起來
通過view.setTag(holder)將引用設置到view中
通過holder,將孩子view設置到此holder中,從而減少以后查詢的次數
④在復用listView中的條目的時候,通過view.getTag(),將view對象轉化為holder,即轉化成相應的引用,方便在下次使用的時候存入集合。
通過view.getTag(holder)獲取引用(需要強轉)
示例代碼:
二、ListView
中數據的分批及分頁加載:
需求:
ListView有一萬條數據,如何顯示;如果將十萬條數據加載到內存,很消耗內存
解決辦法:
優化查詢的數據:先獲取幾條數據顯示到界面上
進行分批處理---à優化了用戶體驗
進行分頁處理---à優化了內存空間
說明:
一般數據都是從數據庫中獲取的,實現分批(分頁)加載數據,就需要在對應的DAO中有相應的分批(分頁)獲取數據的方法,如findPartDatas ()
1、準備數據:
在dao中添加分批加載數據的方法:findPartDatas ()
在適配數據的時候,先加載第一批的數據,需要加載第二批的時候,設置監聽檢測何時加載第二批
2、設置ListView的滾動監聽器:setOnScrollListener(new OnScrollListener{….})
①、在監聽器中有兩個方法:滾動狀態發生變化的方法(onScrollStateChanged)和listView被滾動時調用的方法(onScroll)
②、在滾動狀態發生改變的方法中,有三種狀態:
手指按下移動的狀態:SCROLL_STATE_TOUCH_SCROLL: //觸摸滑動
慣性滾動(滑翔(flgin)狀態):SCROLL_STATE_FLING: //滑翔
靜止狀態:SCROLL_STATE_IDLE://靜止
3、對不同的狀態進行處理:
分批加載數據,只關心靜止狀態:關心最后一個可見的條目,如果最后一個可見條目就是數據適配器(集合)里的最后一個,此時可加載更多的數據。在每次加載的時候,計算出滾動的數量,當滾動的數量大于等于總數量的時候,可以提示用戶無更多數據了。
示例代碼:
/給listview注冊一個滾動的監聽器.
lv_call_sms_safe.setOnScrollListener(newOnScrollListener() {
? ? ? ? ? ? ? ? ? ? ?//當滾動狀體發生變化的時候調用的方法
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ? publicvoid onScrollStateChanged(AbsListView view, int scrollState) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?switch(scrollState) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?caseSCROLL_STATE_FLING: //滑翔
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? caseSCROLL_STATE_IDLE: //靜止
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//在靜止狀態下關心最后一個可見的條目如果最后一個可見條目就是數據適配器里面的最后一個,加載更多數據.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? intposition = lv_call_sms_safe.getLastVisiblePosition(); //位置從0開始
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? intsize = blackNumbers.size();//從1開始的.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(position == (size - 1)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Log.i(TAG,"拖動到了最后一個條目,加載更多數據");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?startIndex+= maxNumber;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(startIndex>=totalCount){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Toast.makeText(getApplicationContext(),"沒有更多數據了..",0).show();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fillData();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ?caseSCROLL_STATE_TOUCH_SCROLL: //觸摸滑動
? ? ? ? ? ? ? ? ? ? ? ? ? break;
}
}
//當listview被滾動的時候調用的方法
@Override
publicvoid onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, inttotalItemCount) {
}
});
/**
*填充數據
*/
? ? ? ? ? ? ? ? private void fillData() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //通知用戶一下正在獲取數據
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ll_loading.setVisibility(View.VISIBLE);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?newThread() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?publicvoid run() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //獲取全部的黑名單號碼
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(blackNumbers != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?blackNumbers.addAll(dao.findPartBlackNumbers(startIndex,maxNumber));
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?blackNumbers= dao.findPartBlackNumbers(startIndex,maxNumber);
? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ?handler.sendEmptyMessage(0);
? ? ? ? ? ? ? ? ?//lv_call_sms_safe.setAdapter(new CallSmsSafeAdapter());
? ? ? ? ? ? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? ? ? ? ?}.start();
}
三、復雜ListView的處理:(待進一步總結)
說明:
listView的界面顯示是通過getCount和getView這兩個方法來控制的
getCount:返回有多少個條目
getView:返回每個位置條目顯示的內容
提供思路:
對于含有多個類型的item的優化處理:由于ListView只有一個Adapter的入口,可以定義一個總的Adapter入口,存放各種類型的Adapter
以安全衛士中的進程管理的功能為例。效果如圖:
1、定義兩個(或多個)集合
每個集合中存入的是對應不同類型的內容(這里為:用戶程序(userAppinfos)和系統程序的集合(systemAppinfos))
2、在初始化數據(填充數據)中初始化兩個集合
如,此處是在fillData方法中初始化
3、在數據適配器中,復寫對應的方法
getCount():計算所有需要顯示的條目個數,這里包括listView和textView
getView():對顯示在不同位置的條目進行if處理
4、數據類型的判斷
需要注意的是,在復用view的時候,需要對convertView進行類型判斷,是因為這里含有各種不同類型的view,在view滾動顯示的時候,對于不同類型的view不能復用,所有需要判斷
示例代碼:
獲取條目個數
類型判斷:
getView中條目位置的選擇:
四、ListView中圖片的優化:
1、處理圖片的方式:
如果自定義Item中有涉及到圖片等等的,一定要狠狠的處理圖片,圖片占的內存是ListView項中最惡心的,處理圖片的方法大致有以下幾種:
①、不要直接拿路徑就去循環decodeFile();使用Option保存圖片大小、不要加載圖片到內存去
②、拿到的圖片一定要經過邊界壓縮
③、在ListView中取圖片時也不要直接拿個路徑去取圖片,而是以WeakReference(使用WeakReference代替強引用。
比如可以使用WeakReference?mContextRef)、SoftReference、WeakHashMap等的來存儲圖片信息,是圖片信息不是圖片哦!
④、在getView中做圖片轉換時,產生的中間變量一定及時釋放
2、異步加載圖片基本思想:
1)、先從內存緩存中獲取圖片顯示(內存緩沖)
2)、獲取不到的話從SD卡里獲取(SD卡緩沖)
3)、都獲取不到的話從網絡下載圖片并保存到SD卡同時加入內存并顯示(視情況看是否要顯示)
原理:
優化一:先從內存中加載,沒有則開啟線程從SD卡或網絡中獲取,這里注意從SD卡獲取圖片是放在子線程里執行的,否則快速滑屏的話會不夠流暢。
優化二:于此同時,在adapter里有個busy變量,表示listview是否處于滑動狀態,如果是滑動狀態則僅從內存中獲取圖片,沒有的話無需再開啟線程去外存或網絡獲取圖片。
優化三:ImageLoader里的線程使用了線程池,從而避免了過多線程頻繁創建和銷毀,有的童鞋每次總是new一個線程去執行這是非常不可取的,好一點的用的AsyncTask類,其實內部也是用到了線程池。在從網絡獲取圖片時,先是將其保存到sd卡,然后再加載到內存,這么做的好處是在加載到內存時可以做個壓縮處理,以減少圖片所占內存。
Tips:這里可能出現圖片亂跳(錯位)的問題:
圖片錯位問題的本質源于我們的listview使用了緩存convertView,假設一種場景,一個listview一屏顯示九個item,那么在拉出第十個item的時候,事實上該item是重復使用了第一個item,也就是說在第一個item從網絡中下載圖片并最終要顯示的時候,其實該item已經不在當前顯示區域內了,此時顯示的后果將可能在第十個item上輸出圖像,這就導致了圖片錯位的問題。所以解決之道在于可見則顯示,不可見則不顯示。在ImageLoader里有個imageViews的map對象,就是用于保存當前顯示區域圖像對應的url集,在顯示前判斷處理一下即可。
Adapter示例代碼:
public class LoaderAdapter extendsBaseAdapter{
? ? ? ? ? private static final String TAG = "LoaderAdapter";
? ? ? ? ? private boolean mBusy = false;?????????? //是否處于滑動中
? ? ? ? ? public void setFlagBusy(boolean busy) {
? ? ? ? ? this.mBusy = busy;
}
? ? ? ? ? private ImageLoader mImageLoader;
? ? ? ? ? private int mCount;
? ? ? ? ? private Context mContext;
? ? ? ? ? private String[] urlArrays;
? ? ? ? ? public LoaderAdapter(int count, Context context, String[]url) {
? ? ? ? ?this.mCount = count;
? ? ? ? ?this.mContext = context;
? ? ? ? ?urlArrays = url;
? ? ? ? ? mImageLoader = new ImageLoader(context);
}
? ? ? ? ? public ImageLoader getImageLoader(){
? ? ? ? ? return mImageLoader;
}
? ? ? ? ?@Override
? ? ? ? ?public int getCount() {
? ? ? ? ?return mCount;
}
? ? ? ?@Override
? ? ? ?public Object getItem(int position) {
? ? ? ?return position;
}
? ? ? ? @Override
? ? ? ? ?public long getItemId(int position) {
return position;
}
? ? ? ? @Override
? ? ? ? ?public View getView(int position, View convertView, ViewGroupparent) {
? ? ? ? ?ViewHolder viewHolder = null;
? ? ? ? ?if (convertView == null) {//加載新創建的view
? ? ? ? ?convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item,null);
? ? ? ? ?viewHolder = new ViewHolder();
? ? ? ? ?viewHolder.mTextView = (TextView) convertView.findViewById(R.id.tv_tips);
? ? ? ? ? viewHolder.mImageView = (ImageView)convertView.findViewById(R.id.iv_image);
? ? ? ? ?convertView.setTag(viewHolder);
? ? ? ? ?} else {
? ? ? ? ?viewHolder= (ViewHolder) convertView.getTag();
}
? ? ? ? ?String url = "";
? ? ? ? ?url = urlArrays[position % urlArrays.length];
? ? ? ? ?viewHolder.mImageView.setImageResource(R.drawable.ic_launcher);
? ? ? ? ?if (!mBusy) {
? ? ? ? mImageLoader.DisplayImage(url, viewHolder.mImageView, false);
? ? ? ? ?viewHolder.mTextView.setText("--" + position + "--IDLE||TOUCH_SCROLL");
? ? ? ? } else {
? ? ? ? mImageLoader.DisplayImage(url, viewHolder.mImageView, true);
? ? ? ? viewHolder.mTextView.setText("--" + position +"--FLING");
}
? ? ? //復用歷史緩存view
? ? ? return convertView;
}
? ? ? ?static class ViewHolder {
? ? ? ? ? TextView mTextView;
? ? ? ? ?ImageView mImageView;
? ? ? }
}
3、內存緩沖機制:
首先限制內存圖片緩沖的堆內存大小,每次有圖片往緩存里加時判斷是否超過限制大小,超過的話就從中取出最少使用的圖片并將其移除。
當然這里如果不采用這種方式,換做軟引用也是可行的,二者目的皆是最大程度的利用已存在于內存中的圖片緩存,避免重復制造垃圾增加GC負擔;OOM溢出往往皆因內存瞬時大量增加而垃圾回收不及時造成的。只不過二者區別在于LinkedHashMap里的圖片緩存在沒有移除出去之前是不會被GC回收的,而SoftReference里的圖片緩存在沒有其他引用保存時隨時都會被GC回收。所以在使用LinkedHashMap這種LRU算法緩存更有利于圖片的有效命中,當然二者配合使用的話效果更佳,即從LinkedHashMap里移除出的緩存放到SoftReference里,這就是內存的二級緩存。
本例采用的是LRU算法,先看看MemoryCache的實現
五、ListView的其他優化:
1、盡量避免在BaseAdapter中使用static來定義全局靜態變量:
static是Java中的一個關鍵字,當用它來修飾成員變量時,那么該變量就屬于該類,而不是該類的實例。所以用static修飾的變量,它的生命周期是很長的,如果用它來引用一些資源耗費過多的實例(比如Context的情況最多),這時就要盡量避免使用了。
2、盡量使用getApplicationContext:
如果為了滿足需求下必須使用Context的話:Context盡量使用Application Context,因為Application的Context的生命周期比較長,引用它不會出現內存泄露的問題
3、盡量避免在ListView適配器中使用線程:
因為線程產生內存泄露的主要原因在于線程生命周期的不可控制。之前使用的自定義ListView中適配數據時使用AsyncTask自行開啟線程的,這個比用Thread更危險,因為Thread只有在run函數不結束時才出現這種內存泄露問題,然而AsyncTask內部的實現機制是運用了線程執行池(ThreadPoolExcutor),這個類產生的Thread對象的生命周期是不確定的,是應用程序無法控制的,因此如果AsyncTask作為Activity的內部類,就更容易出現內存泄露的問題。解決辦法如下:
①、將線程的內部類,改為靜態內部類。
②、在線程內部采用弱引用保存Context引用
示例代碼:
public?abstract?class?WeakAsyncTask?extends?AsyncTask?{
? ? ? ? ? ? ? ? ? ? protected?WeakReference?mTarget;
? ? ? ? ? ? ? ? ? ? public?WeakAsyncTask(WeakTarget?target)?{
? ? ? ? ? ? ? ? ? ? mTarget?=?new?WeakReference(target);
}
? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ?protected?final?void?onPreExecute()?{
? ? ? ? ? ? ? ? ? ?final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? this.onPreExecute(target);
}
}
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ?protected?final?Result?doInBackground(Params...?params)?{
? ? ? ? ? ? ? ? ? ? final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? ? if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? ? return?this.doInBackground(target,?params);
? ? ? ? ? ? ? ? ? ?}?else?{
? ? ? ? ? ? ? ? ? return?null;
? ? ? ? ? ? ? ? ? }
}
? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ?protected?final?void?onPostExecute(Result?result)?{
? ? ? ? ? ? ? ? ? ?final?WeakTarget?target?=?mTarget.get();
? ? ? ? ? ? ? ? ? ?if?(target?!=?null)?{
? ? ? ? ? ? ? ? ? ?this.onPostExecute(target,?result);
}
}
? ? ? ? ? ? ? ? ?protected?void?onPreExecute(WeakTarget?target)?{
? ? ? ? ? ? ? ? ? ? ? ?//?No?default?action
}
? ? ? ? ? ? ? protected?abstract?Result?doInBackground(WeakTarget?target,?Params...?params);
? ? ? ? ? ? ? ? protected?void?onPostExecute(WeakTarget?target,?Result?result)?{
? ? ? ? ? ? ? ?/ /?No?default?action
}
}
六、ScrollView和ListView的沖突問題:【摘自網絡】
解決方法之一:
在ScrollView添加一個ListView會導致listview控件顯示不全,這是因為兩個控件的滾動事件沖突導致。所以需要通過listview中的item數量去計算listview的顯示高度,從而使其完整展示,如下提供一個方法供大家參考。
示例代碼:
public voidsetListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() *(listAdapter.getCount() - 1));
params.height += 5;//if without this statement,the listview will bea little short
listView.setLayoutParams(params);
}