最近在做android相關的開發,ListView中有一個圖片錯位的問題,今天查了很多人寫的一些東西,所以記錄下來,算是一種加深理解吧。
ListView是一個非常常用的控件,功能可以擴展的很豐富,而且與GridView有很多相似的地方。都可以存放大量數據。而且當我們需要比較復雜的布局時,一般用SimpleAdapter,或者繼承BaseAdapter自己重寫。
如果是繼承ArrayAdapter,SimpleAdapter的時候,由於父類本身維護了一個List,所以當有數據更新的時候,盡量用adapter的add,這樣可以保證getCount()返回的值是正確。
這裡面涉及到的一個比較重要的重寫函數就是getView
在getView中有三種方式實現view的返回。
第一種方式:
最簡單也是最容易理解的是每一個view都通過inflate生成一個新的view進行返回
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View item = inflater.inflate(R.layout.list_item_icon_text, null);
((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
return item;
}
這種方式,在數據量小的時候,劣勢不明顯,但是當一個listview裡面有大量條目時,這種方式就顯得非常浪費。因為每一次item的出現,消失或者更新時,都需要重新inflate。給內存造成了很大的消耗。
那麼還有什麼方式可以節省開銷呢?
第二種方式:
我們看看getView函數裡面conertview的是干啥的。查看官方文檔後發現:
convertView - The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see Adapter.getViewTypeCount() and Adapter.getItemViewType(int)).
注:
getViewTypeCount()是用來當listview中有不同的種類的item,比如分割線之類的時候,用getItemViewType返回某個item的類型,然後坐不同的
原來converView是用來重用view的。經過查找別的資料。找到了一張比較經典的圖如下:
這張圖清楚的描述了listview是如何重用view的。listview通過getview分別請求所有可見項目。此時converview是空的。
當item1滑動出屏幕後,item8從下面滑動出來,此時又要調用getview,但是此時的converview已經不再為空了,而是上次一劃出屏幕的item1,此時我們只需要重新修改下item1的數據,而不必重新創建一個新的view。
這樣就節省了內存。
那麼由此我們得到了第二種方式
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item, parent, false);
}
((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(
(position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
這種方式裡面,我們通過converview找到對應需要修改的text和ico,然後修改數據,進行返回。
有人會說,上面不已經解決了重用的問題了麼,還有第三種方式?
那麼我們下面看看google推薦的第三種方式是怎麼實現的。
static class ViewHolder {
TextView text;
ImageView icon;
}
這裡定義了一個內部靜態類,
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text,
parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
然後用convertView.setTag(holder)的方式將之前通過findViewById找到的textview與ImageView放入converivew對象中。
這樣當再次重用converview的時候,就不必再次利用findViewById來查找了,省掉了開銷。