精選分析listview亂序的原因

最大的想法就是:在進行listview複用的時候,每一個item要進行特殊處理的時候,其他剩下的item也得進行相應的變化處理:三種方法:

                                 1.進行if和else設置處理
                                 2.設置tag將和對象進行一一綁定;
                                 3.設置標誌位;


ListView 滑動時圖片(或背景色)重複混亂

在討論圖片重複問題之前,先來看看背景色重複的問題:

想要達到的效果:品牌列表的前三項的背景色設置爲磚紅色,剩餘其他項的背景色爲默認色。

實際效果:滑動列表後,後面應該顯示默認背景色的item也會出現磚紅色背景

背景色重複的效果圖如下:

有關ListView優化機制及滑動時數據錯亂有關問題的討論

通過代碼來進一步分析,下面是處理列表數據顯示的adapter類名爲“TestDisorderListAdapter”繼承BaseAdapter。brandInfoList是一個列表,包含了一個個BrandItemInfo對象(data modle):

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. package com.aliao.myandroiddemo.adapter;  
  2.   
  3. import android.content.Context;  
  4. import android.util.Log;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.BaseAdapter;  
  9. import android.widget.CheckBox;  
  10. import android.widget.TextView;  
  11.   
  12. import com.aliao.myandroiddemo.R;  
  13. import com.aliao.myandroiddemo.domain.BrandItemInfo;  
  14.   
  15. import java.util.List;  
  16.   
  17. /** 
  18.  * Created by liaolishuang on 14-3-31. 
  19.  */  
  20. public class TestDisorderListAdapter extends BaseAdapter{  
  21.   
  22.     private Context context;  
  23.     private List<BrandItemInfo> brandInfoList;  
  24.     private final String TAG = "disorderlist";  
  25.   
  26.     public TestDisorderListAdapter(Context context, List<BrandItemInfo> list){  
  27.   
  28.         this.context = context;  
  29.         brandInfoList = list;  
  30.   
  31.     }  
  32.   
  33.     @Override  
  34.     public int getCount() {  
  35.         return brandInfoList.size();  
  36.     }  
  37.   
  38.     @Override  
  39.     public Object getItem(int i) {  
  40.         return null != brandInfoList?brandInfoList.get(i):null;  
  41.     }  
  42.   
  43.     @Override  
  44.     public long getItemId(int i) {  
  45.         return i;  
  46.     }  
  47.   
  48.     private class ViewHolder{  
  49.         private TextView brandEnNameTv;  
  50.         private TextView brandChNameTv;  
  51.         private CheckBox followCheckBox;  
  52.     }  
  53.   
  54.     @Override  
  55.     public View getView(int i, View view, ViewGroup viewGroup) {  
  56.   
  57.         ViewHolder viewHolder = null;  
  58.   
  59.         if(null == view){  
  60.               
  61.             LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  62.             view = inflater.inflate(R.layout.item_testdisorderitem,null);  
  63.   
  64.             viewHolder = new ViewHolder();  
  65.             viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);  
  66.             viewHolder.brandEnNameTv= (TextView) view.findViewById(R.id.item_enName_txt);  
  67.   
  68.             view.setTag(viewHolder);  
  69.   
  70.         }else {  
  71.              
  72.             viewHolder = (ViewHolder) view.getTag();  
  73.         }  
  74.   
  75.         BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);  
  76.         viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());  
  77.         viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());  
  78.   
  79.         if(i < 3){  
  80.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  81.         }  
  82.          
  83.         return view;  
  84.     }  
  85.   
  86. }  

爲了設置品牌列表的前三項的背景色,在getView()方法中加入代碼

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. if(i < 3){  
  2.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  3.         }  

i是代表每一行的position,由0開始。當向上滑動列表時,i的值隨着滑動行的改變而遞增,沒有任何問題,但是爲什麼只有i<3的情況才改變顏色,其他項還會有背景色的改變呢?

在Adapters and Holder Pattern部分我們已經瞭解了listview的緩存優化機制,滾出屏幕的視圖會被緩存下來並被複用。我們以爲只要設置i<3就可以讓前3項變色,其他項自然就是背景色,卻忽略了,除了前三項之外的某些行會去複用前三項的視圖因此也就會有相同的背景色。所以當i>=3對應的某些行由於複用了i<3對應的行,造成了背景色也爲磚紅色。

解決方法就是必須對i不小於3的情況進行處理,把它設置爲背景色即:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. if(i < 3){  
  2.             view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));  
  3.         }else{  
  4.             view.setBackgroundColor(context.getResources().getColor(R.color.background));  
  5.         }  
對於圖片重複的問題,其實是一樣的道理,舉這麼個例子:

評論列表中會顯示頭像和評論內容,從手機接口端讀取評論內容,其中的一個字段是用戶頭像的下載地址,因爲有的用戶有上傳頭像有的沒有,如果用戶沒有上傳頭像,那麼返回的用戶頭像的字段爲空字符串。在getView()裏面就需要判斷當用戶頭像不爲空字符串的時候去設置頭像,否則爲默認頭像,該默認頭像由應用程序本地存儲的圖片,在xml文件的ImageView裏設置了。正確的代碼片段爲:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1.        if(!brandItemInfo.getBrandImage().equal("")){  
  2.            //根據url loadImage  
  3.        }else{  
  4.            viewHolder.brandLogo.setImageResource(R.drawable.ic_default_head);  
  5.        }         
如果不進行else處理,那麼原先顯示默認頭像的imageView在list滑動後會重複顯示其他頭像的圖片。

ListView滑動後出現checkbox選取錯位

有關ListView優化機制及滑動時數據錯亂有關問題的討論

我遇到過的checkbox選取錯位是列表上有checkbox,選取某個checkbox爲選中狀態,然後滑動列表發現其他未選擇過的checkbox也變成了選中狀態。重現該問題只需要在item的xml file中加入checkbox,然後java代碼裏不對checkbox做任何處理,測試時直接選中某個checkbox,滑動列表就會看到某些行會重複出現checkbox被選中,這種情況聽起來和上一節所說的listview緩存優化問題引起重複錯亂如出一轍(當然這只是我重現問題最簡單的方式)。但是之所以把checkbox單獨拿出來說是因爲他與上述的問題並不完全相同,它涉及到列表項上的某個view狀態的改變會影響到與該列表項對應的數據對象的改變。滑動list後checkbox選中狀態出現問題,是由於rowview和對象一一對應,對象裏的狀態值沒有相應改變。

在列表上像checkbox、toglebutton這種控件,它的選取狀態會影響到數據的變化。例如在列表項上的checkbox,如果是選中後,該列表項對應的對象裏的數據就會發生改變。這就涉及到數據模型與列表項之間的"通訊",當列表項上的checkbox狀態改變,要相應地修改改行對應對象的數據。

The row can also containviews which interact with the underlying data model via the adapter. For example, you can have aCheckbox in your row layout and if theCheckbox is selected, the underlying data is changed.

Frequently you need to select items in yourListView. As the row of theListView are getting recycled you cannot store the selection on theView level.

To persist the selection you have to update your data model with the selected state.

To update the data model in yourListView you define your ownAdapter class. In this adapter class you attach a listener to theView which is responsible for selecting the model element. If selected you update the state in the model which you can add as a tag to the View to have access to it.”

我們在處理listview帶有像checkbox,toglebutton這類控件的時候,需要監聽控件的狀態,一旦狀態發生改變,就去改變列表項對應的對象數據。通過setTag()方法把checkbox與對象綁定在一起,一旦狀態改變就在監聽方法裏通過getTag()方法將對象取出,更改對象中選中字段的狀態值。

代碼實現:

domain中的實體類BrandItemInfo類:

增加了isSelected變量用來保存checkbox的選取狀態

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. package com.aliao.myandroiddemo.domain;  
  2.   
  3. /** 
  4.  * Created by liaolishuang on 14-3-31. 
  5.  */  
  6. public class BrandItemInfo {  
  7.   
  8.     private String brandEnName;  
  9.     private String brandChName;  
  10.     //    private String brandImage;  
  11.     private int brandImage;  
  12.     private boolean selected;  
  13.   
  14.     public boolean isSelected() {  
  15.         return selected;  
  16.     }  
  17.   
  18.     public void setSelected(boolean selected) {  
  19.         this.selected = selected;  
  20.     }  
  21.   
  22.     public void setBrandImage(int brandImage) {  
  23.         this.brandImage = brandImage;  
  24.     }  
  25.   
  26.     public void setBrandChName(String brandChName) {  
  27.         this.brandChName = brandChName;  
  28.     }  
  29.   
  30.     public void setBrandEnName(String brandEnName) {  
  31.         this.brandEnName = brandEnName;  
  32.     }  
  33.   
  34.     public String getBrandChName() {  
  35.         return brandChName;  
  36.     }  
  37.   
  38.     public String getBrandEnName() {  
  39.         return brandEnName;  
  40.     }  
  41.   
  42.     public int getBrandImage() {  
  43.         return brandImage;  
  44.     }  
  45.   
  46. }  

Adapter類:

將每行的checkbox與該行相對應的brandItemInfo對象通過setTag()方法關聯起來。並增加了checkbox的監聽,一旦監聽到選取狀態的改變,就通過getTag()方法取出對象來更新brandItemInfo對象的isSelected字段的值。

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. package com.aliao.myandroiddemo.adapter;  
  2.   
  3. import android.content.Context;  
  4. import android.util.Log;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.BaseAdapter;  
  9. import android.widget.CheckBox;  
  10. import android.widget.CompoundButton;  
  11. import android.widget.ImageView;  
  12. import android.widget.TextView;  
  13.   
  14. import com.aliao.myandroiddemo.R;  
  15. import com.aliao.myandroiddemo.domain.BrandItemInfo;  
  16.   
  17. import java.util.List;  
  18.   
  19. /** 
  20.  * Created by liaolishuang on 14-3-31. 
  21.  */  
  22. public class TestDisorderListAdapter extends BaseAdapter{  
  23.   
  24.     private Context context;  
  25.     private List<BrandItemInfo> brandInfoList;  
  26.     private final String TAG = "disorderlist";  
  27.   
  28.     public TestDisorderListAdapter(Context context, List<BrandItemInfo> list){  
  29.   
  30.         this.context = context;  
  31.         brandInfoList = list;  
  32.   
  33.     }  
  34.   
  35.     @Override  
  36.     public int getCount() {  
  37.         return brandInfoList.size();  
  38.     }  
  39.   
  40.     @Override  
  41.     public Object getItem(int i) {  
  42.         return null != brandInfoList?brandInfoList.get(i):null;  
  43.     }  
  44.   
  45.     @Override  
  46.     public long getItemId(int i) {  
  47.         return i;  
  48.     }  
  49.   
  50.     private class ViewHolder{  
  51.         private TextView brandEnNameTv;  
  52.         private TextView brandChNameTv;  
  53.         private CheckBox followCheckBox;  
  54.         private ImageView brandLogo;  
  55.     }  
  56.   
  57.   
  58.     @Override  
  59.     public View getView(int i, View view, ViewGroup viewGroup) {  
  60.   
  61.         ViewHolder viewHolder = null;  
  62.         BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);  
  63.   
  64.         if(null == view){  
  65.             
  66.             LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  67.             view = inflater.inflate(R.layout.item_testdisorderitem,null);  
  68.   
  69.             viewHolder = new ViewHolder();  
  70.             viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);  
  71.             viewHolder.brandEnNameTv = (TextView) view.findViewById(R.id.item_enName_txt);  
  72.             viewHolder.brandLogo = (ImageView) view.findViewById(R.id.item_brandLogo_imagev);  
  73.             viewHolder.followCheckBox = (CheckBox) view.findViewById(R.id.item_follow_checkbox);  
  74.             final ViewHolder finalViewHolder = viewHolder;  
  75.             viewHolder.followCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
  76.                 @Override  
  77.                 public void onCheckedChanged(CompoundButton compoundButton, boolean b) {  
  78.                     BrandItemInfo info = (BrandItemInfo) finalViewHolder.followCheckBox.getTag();  
  79.                     info.setSelected(compoundButton.isChecked());  
  80.                 }  
  81.             });  
  82.             view.setTag(viewHolder);  
  83.             viewHolder.followCheckBox.setTag(brandItemInfo);  
  84.              
  85.         }else {  
  86.              
  87.             viewHolder = (ViewHolder) view.getTag();  
  88.             viewHolder.followCheckBox.setTag(brandItemInfo);  
  89.         }  
  90.   
  91.         viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());  
  92.         viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());  
  93.         viewHolder.brandLogo.setImageResource(brandItemInfo.getBrandImage());  
  94.         viewHolder.followCheckBox.setChecked(brandItemInfo.isSelected());  
  95.   
  96.         return view;  
  97.     }  
  98.   
  99. }  


好文分享:

Lars Vogel的

Using lists in Android(ListView) 對ListView的使用做了非常系統完整的講解,由淺到深很適合閱讀。閱讀了這篇文章後,對ListView的緩存優化機制有了更進一步的理解,從而有了本篇blog的Adapter and Holder Pattern作爲閱讀後的總結。

農民伯伯的:

ListView性能優化之視圖緩存 介紹了Google I/O提供的優化Adapter方案,並對這些方案進行了測試。

ListView性能優化之視圖緩存續 介紹了新浪微博中主界面的做法及測試數據
發佈了185 篇原創文章 · 獲贊 19 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章