且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

【解决自定义多选ListView乱序问题】

更新时间:2022-08-13 14:29:15


解决Android自定义多选ListView乱序问题

正常情况下,自定义带CheckedBoxListView的时候,如果Items的数目超过一个屏幕,你会发现被checkedItem在屏幕滚动之后会出现乱序现象。

出现这种乱序现象的原因可以参考如下博文

http://haking.iteye.com/blog/1147404

如上述,Android为我们提供了一个ListView的缓冲机制,在屏幕滚动时,会重新利用被遮挡(即上一个屏幕的Items)的View进行更新显示。这也难怪我们的选择状态的显示会不尽人意。

既然知道了其原因,解决办法也就应运而生了。

【办法一】如网上所说,禁用android提供的缓冲机制,即在getView开头人为使得convertView变为null,从而强制为每一个item创建一个新的view用于显示。

 

但是当listview要显示的量比较大时,这个方法就显得有点臃肿不堪。

Bill今早根据网上各位仁兄的方法,琢磨了个比较简单的解决办法(之所以简单,是因为这个方法只针对具有CheckedBox的ListView,而对于要保存ImageView之类的ListView,除网上的讲解外,bill自己还没有想出更好的办法),不知道是否对大家有所帮助。

 

【办法二】既然我们因为数据量太大的原因确实有必要使用android自带的缓冲机制,那么禁用它看来是不现实了。现在问题的思考点就转到“在这种缓冲机制存在的情况下,如何能够正确地显示checked的状态?”,或者更加明白一点“在缓冲机制存在的情况下,CheckedBox的状态会发生改变,如何保存这些状态并在之后正确还原?”

Bill的思路如下:android要更新回收站中的item就让它更新吧,我用另外的空间记录每个CheckedBox之前的选中状态,等到要显示对应CheckedBox的时候再根据之前记录的状态手动设置CheckedBox的状态即可。

一下是本解决方案的Demo

 


  1. <!-- checked listview item layout --> 
  2. <?xml version="1.0" encoding="utf-8"?> 
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  4.     android:orientation="horizontal" android:layout_width="fill_parent" 
  5.     android:layout_height="fill_parent"> 
  6.  
  7.     <CheckBox android:id="@+id/checked_box" android:layout_width="wrap_content" 
  8.         android:layout_height="wrap_content" android:focusable="false"></CheckBox> 
  9.  
  10.     <TextView android:id="@+id/text_view" android:layout_width="wrap_content" 
  11.         android:layout_height="wrap_content"></TextView> 
  12.  
  13. </LinearLayout> 

 


  1. <!-- main activity layout --> 
  2. <?xml version="1.0" encoding="utf-8"?> 
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  4.     android:orientation="vertical" android:layout_width="fill_parent" 
  5.     android:layout_height="fill_parent"> 
  6.     <Button android:id="@+id/bt_show_checked" android:layout_width="wrap_content" 
  7.         android:layout_height="wrap_content" android:text="哪些项被选中了?"></Button> 
  8.     <ListView android:id="@+id/lv_demo" android:layout_width="fill_parent" 
  9.         android:layout_height="fill_parent"></ListView> 
  10. </LinearLayout> 

 


  1. /** 
  2. * 自定义CheckedListViewAdapter 
  3. * 关键部分有所注释 
  4. */ 
  5. package com.billhoo.study; 
  6.  
  7. import java.util.ArrayList; 
  8. import java.util.List; 
  9.  
  10. import android.view.LayoutInflater; 
  11. import android.view.View; 
  12. import android.view.ViewGroup; 
  13. import android.widget.BaseAdapter; 
  14. import android.widget.CheckBox; 
  15. import android.widget.TextView; 
  16.  
  17. // 自定义适配器,用于实现将CheckBox作为ListView的item 
  18. public class CheckedListViewAdapter<ItemTy> extends BaseAdapter { 
  19.   // 状态数组,用于跟踪ListView中每一个CheckedBox的选中状态 
  20.   private ArrayList<Boolean> mCheckedStates = null
  21.  
  22.   private LayoutInflater mInflater = null
  23.   private List<ItemTy> mItemList = null
  24.  
  25.   public CheckedListViewAdapter(LayoutInflater inflater, List<ItemTy> list) { 
  26.     mCheckedStates = new ArrayList<Boolean>(); 
  27.     this.mInflater = inflater; 
  28.     this.mItemList = list; 
  29.  
  30.     // 初始化所有checked box选中状态为false 
  31.     for (int i = 0; i < mItemList.size(); ++i) 
  32.       mCheckedStates.add(false); 
  33.   } 
  34.  
  35.   public ArrayList<Boolean> getCheckedState() { 
  36.     return mCheckedStates; 
  37.   } 
  38.  
  39.   @Override 
  40.   public int getCount() { 
  41.     return mItemList.size(); 
  42.   } 
  43.  
  44.   @Override 
  45.   public Object getItem(int position) { 
  46.     return mItemList.get(position); 
  47.   } 
  48.  
  49.   @Override 
  50.   public long getItemId(int position) { 
  51.     return position; 
  52.   } 
  53.  
  54.   @Override 
  55.   public View getView(int position, View convertView, ViewGroup parent) { 
  56.     ItemViewHolder holder = null
  57.     if (convertView == null) { 
  58.       holder = new ItemViewHolder(); 
  59.       convertView = mInflater.inflate(R.layout.checked_listview_item, null); 
  60.       final View view = convertView; 
  61.       holder.checkBox = (CheckBox) view.findViewById(R.id.checked_box); 
  62.       holder.textView = (TextView) view.findViewById(R.id.text_view); 
  63.       convertView.setTag(holder); 
  64.     } else { 
  65.       holder = (ItemViewHolder) convertView.getTag(); 
  66.     } 
  67.  
  68.     // 每次getView的时候都手动设置位于当前屏幕中的checkedBox的选定状态。 
  69.     holder.checkBox.setChecked(mCheckedStates.get(position)); 
  70.     holder.textView.setText(mItemList.get(position).toString()); 
  71.  
  72.     final int pos = position; 
  73.     // 当checked box被点击,即选定状态发生改变时,更新状态List 
  74.     holder.checkBox.setOnClickListener(new CheckBox.OnClickListener() { 
  75.       @Override 
  76.       public void onClick(View v) { 
  77.         CheckBox cb = (CheckBox) v; 
  78.         mCheckedStates.set(pos, cb.isChecked()); 
  79.       } 
  80.     }); 
  81.  
  82.     return convertView; 
  83.   } 
  84.  
  85.   protected class ItemViewHolder { 
  86.     public CheckBox checkBox = null
  87.     public TextView textView = null
  88.   } 

 


  1. /** 
  2. * main activity 
  3. */ 
  4. package com.billhoo.study; 
  5.  
  6. import java.util.ArrayList; 
  7. import java.util.List; 
  8.  
  9. import android.app.Activity; 
  10. import android.os.Bundle; 
  11. import android.view.View; 
  12. import android.widget.Button; 
  13. import android.widget.ListView; 
  14. import android.widget.Toast; 
  15.  
  16. public class CheckedListViewAdapterActivity extends Activity { 
  17.  
  18.   private Button btShowCheckState = null
  19.   private ListView lvDemo = null
  20.   CheckedListViewAdapter<TestData> adapter = null
  21.  
  22.   @Override 
  23.   public void onCreate(Bundle savedInstanceState) { 
  24.     super.onCreate(savedInstanceState); 
  25.     setContentView(R.layout.main); 
  26.     btShowCheckState = (Button) findViewById(R.id.bt_show_checked); 
  27.     lvDemo = (ListView) findViewById(R.id.lv_demo); 
  28.   } 
  29.  
  30.   @Override 
  31.   public void onStart() { 
  32.     super.onStart(); 
  33.     btShowCheckState.setOnClickListener(new Button.OnClickListener() { 
  34.       @Override 
  35.       public void onClick(View v) { 
  36.         String checkedItems = new String(); 
  37.         ArrayList<Boolean> states = adapter.getCheckedState(); 
  38.         for (int i = 0; i < states.size(); ++i) { 
  39.           if (states.get(i)) 
  40.             checkedItems += i + " "
  41.         } 
  42.  
  43.         Toast.makeText(getApplicationContext(), checkedItems, 
  44.             Toast.LENGTH_SHORT).show(); 
  45.       } 
  46.     }); 
  47.   } 
  48.  
  49.   @Override 
  50.   public void onResume() { 
  51.     super.onResume(); 
  52.     List<TestData> dataList = new ArrayList<TestData>(); 
  53.  
  54.     for (int i = 0; i < 20; ++i) { 
  55.       dataList.add(new TestData(i, "这是第" + i + "项数据")); 
  56.     } 
  57.  
  58.     adapter = new CheckedListViewAdapter<TestData>(getLayoutInflater(), 
  59.         dataList); 
  60.  
  61.     lvDemo.setAdapter(adapter); 
  62.   } 
  63.  
  64.   class TestData { 
  65.     public TestData(Integer dataId, String msg) { 
  66.       this.dataId = dataId; 
  67.       this.msg = msg; 
  68.     } 
  69.  
  70.     @Override 
  71.     public String toString() { 
  72.       return dataId + " " + msg; 
  73.     } 
  74.  
  75.     private Integer dataId = null
  76.     private String msg = null
  77.   } 

以下是测试效果:

【未进行手动处理之前】

点选0、4两项

 

【解决自定义多选ListView乱序问题】

之后向下滚屏,发现并未勾选的9、14项却被勾选了【解决自定义多选ListView乱序问题】

 再滚回第一页,发现0、4项已经被取消,却勾上了第6项【解决自定义多选ListView乱序问题】

【使用方法2之后】

照样勾选0、4项

【解决自定义多选ListView乱序问题】

之后向下滚屏,并未出现上述乱序情况

【解决自定义多选ListView乱序问题】

屏幕回滚并点击统计按钮,得到正确结果

【解决自定义多选ListView乱序问题】

 




     本文转自Bill_Hoo 51CTO博客,原文链接:http://blog.51cto.com/billhoo/818324,如需转载请自行联系原作者