編輯:關於android開發
listview加載的核心是其adapter,本文針對listview加載的性能優化就是對adpter的優化,總共分四個層次:
0、最原始的加載
1、利用convertView
2、利用ViewHolder
3、實現局部刷新
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
〇、最原始的加載
這裡是不經任何優化的adapter,為了看起來方便,把listview的數據直接在構造函數裡傳給adapter了,代碼如下:
1 private class AdapterOptmL0 extends BaseAdapter {
2 private LayoutInflater mLayoutInflater;
3 private ArrayList<Integer> mListData;
4
5 public AdapterOptmL0(Context context, ArrayList<Integer> data) {
6 mLayoutInflater = LayoutInflater.from(context);
7 mListData = data;
8 }
9
10 @Override
11 public int getCount() {
12 return mListData == null ? 0 : mListData.size();
13 }
14
15 @Override
16 public Object getItem(int position) {
17 return mListData == null ? 0 : mListData.get(position);
18 }
19
20 @Override
21 public long getItemId(int position) {
22 return position;
23 }
24
25 @Override
26 public View getView(int position, View convertView, ViewGroup parent) {
27 View viewRoot = mLayoutInflater.inflate(R.layout.listitem, parent, false);
28 if (viewRoot != null) {
29 TextView txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
30 txt.setText(getItem(position) + "");
31 }
32 return viewRoot;
33 }
34 }
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
一、利用convertView
上述代碼的第27行在Eclipse中已經提示警告:
Unconditional layout inflation from view adapter: Should use View Holder pattern (use recycled view passed into this method as the second parameter) for smoother scrolling
這個意思就是說,被移出可視區域的view是可以回收復用的,它作為getview的第二個參數已經傳進來了,所以沒必要每次都從xml裡inflate。
經過優化後的代碼如下:
1 @Override
2 public View getView(int position, View convertView, ViewGroup parent) {
3 if (convertView == null) {
4 convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
5 }
6 if (convertView != null) {
7 TextView txt = (TextView)convertView.findViewById(R.id.listitem_txt);
8 txt.setVisibility(View.VISIBLE);
9 txt.setText(getItem(position) + "");
10 }
11 return convertView;
12 }
上述代碼加了判斷,如果傳入的convertView不為null,則直接復用,否則才會從xml裡inflate。
按照上述代碼,如果手機一屏最多同時顯示5個listitem,則最多需要從xml裡inflate 5 次,比AdapterOptmL0中每個listitem都需要inflate顯然效率高多了。
上述的用法雖然提高了效率,但帶來了一個陷阱,如果復用convertView,則需要重置該view所有可能被修改過的屬性。
舉個例子:
如果第一個view中的textview在getview中被設置成INVISIBLE了,而現在第一個view在滾動過程中出可視區域,並假設它作為參數傳入第十個view的getview而被復用
那麼,在第十個view的getview裡面不僅要setText,還要重新setVisibility,因為這個被復用的view當前處於INVISIBLE狀態!
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
二、利用ViewHolder
從AdapterOptmL0第27行的警告中,我們還可以看到編譯器推薦了一種模型叫ViewHolder,這是個什麼東西呢,先看代碼:
1 private class AdapterOptmL2 extends BaseAdapter {
2 private LayoutInflater mLayoutInflater;
3 private ArrayList<Integer> mListData;
4
5 public AdapterOptmL2(Context context, ArrayList<Integer> data) {
6 mLayoutInflater = LayoutInflater.from(context);
7 mListData = data;
8 }
9
10 private class ViewHolder {
11 public ViewHolder(View viewRoot) {
12 txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
13 }
14 public TextView txt;
15 }
16
17 @Override
18 public int getCount() {
19 return mListData == null ? 0 : mListData.size();
20 }
21
22 @Override
23 public Object getItem(int position) {
24 return mListData == null ? 0 : mListData.get(position);
25 }
26
27 @Override
28 public long getItemId(int position) {
29 return position;
30 }
31
32 @Override
33 public View getView(int position, View convertView, ViewGroup parent) {
34 if (convertView == null) {
35 convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
36 ViewHolder holder = new ViewHolder(convertView);
37 convertView.setTag(holder);
38 }
39 if (convertView != null && convertView.getTag() instanceof ViewHolder) {
40 ViewHolder holder = (ViewHolder)convertView.getTag();
41 holder.txt.setVisibility(View.VISIBLE);
42 holder.txt.setText(getItem(position) + "");
43 }
44 return convertView;
45 }
46 }
從代碼中可以看到,這一步做的優化是用一個類ViewHolder來保存listitem裡面所有找到的子控件,這樣就不用每次都通過耗時的findViewById操作了。
這一步的優化,在listitem布局越復雜的時候效果越為明顯。
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
三、實現局部刷新
OK,到目前為止,listview普遍需要的優化已經做的差不多了,那就該考慮實際使用場景中的優化需求了。
實際使用listview過程中,通常會在後台更新listview的數據,然後調用Adatper的notifyDataSetChanged方法來更新listview的UI。
那麼問題來了,一般情況下,一次只會更新listview的一條/幾條數據,而調用notifyDataSetChanged方法則會把所有可視范圍內的listitem都刷新一遍,這是不科學的!
所以,進一步優化的空間在於,局部刷新listview,話不多說見代碼:
private class AdapterOptmL3 extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private ListView mListView;
private ArrayList<Integer> mListData;
public AdapterOptmL3(Context context, ListView listview, ArrayList<Integer> data) {
mLayoutInflater = LayoutInflater.from(context);
mListView = listview;
mListData = data;
}
private class ViewHolder {
public ViewHolder(View viewRoot) {
txt = (TextView)viewRoot.findViewById(R.id.listitem_txt);
}
public TextView txt;
}
@Override
public int getCount() {
return mListData == null ? 0 : mListData.size();
}
@Override
public Object getItem(int position) {
return mListData == null ? 0 : mListData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.listitem, parent, false);
ViewHolder holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
if (convertView != null && convertView.getTag() instanceof ViewHolder) {
updateView((ViewHolder)convertView.getTag(), (Integer)getItem(position));
}
return convertView;
}
public void updateView(ViewHolder holder, Integer data) {
if (holder != null && data != null) {
holder.txt.setVisibility(View.VISIBLE);
holder.txt.setText(data + "");
}
}
public void notifyDataSetChanged(int position) {
final int firstVisiablePosition = mListView.getFirstVisiblePosition();
final int lastVisiablePosition = mListView.getLastVisiblePosition();
final int relativePosition = position - firstVisiablePosition;
if (position >= firstVisiablePosition && position <= lastVisiablePosition) {
updateView((ViewHolder)mListView.getChildAt(relativePosition).getTag(), (Integer)getItem(position));
} else {
//不在可視范圍內的listitem不需要手動刷新,等其可見時會通過getView自動刷新
}
}
}
修改後的Adapter新增了一個方法 public void notifyDataSetChanged(int position) 可以根據position只更新指定的listitem。
[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
局部刷新番外篇
在局部刷新數據的接口中,實際上還可以再干點事情:listview正在滾動的時候不去刷新。
具體的思路是,如果當前正在滾動,則記住一個pending任務,等listview停止滾動的時候再去刷,這樣不會造成滾動的時候刷新錯亂。代碼如下:

[轉載請保留本文地址:http://www.cnblogs.com/goagent/p/5158064.html]
Android WebView遠程代碼執行漏洞簡析
Android WebView遠程代碼執行漏洞簡析 0x00 本文參考Android WebView 遠程代碼執行漏洞簡析。代碼地址為,https://github.
Android開發Tips(3)
Android開發Tips(3) 1. UIAutomatorViewer 自動化測試是Android測試的趨勢, 穩定\復用, 最常用的工具就是Espresso. 使用
android中菜單(menu)的基本知識,androidmenu
android中菜單(menu)的基本知識,androidmenu(一)選項菜單 1、簡單的創建菜單: 1 @Override 2 public boole
Android開發環境的搭建之Java開發環境的安裝,androidjava
Android開發環境的搭建之Java開發環境的安裝,androidjava(1) 安裝JDK(Java Developer Kit)。下載JDK1.8並安裝