最近在一個項目中,需要在ListView的item中加入CheckBox,但是遇到的一個問題是上下滑動的時候如果有選擇了的CheckBox,就會出現選擇項錯誤的問題,下面將個人的解決方法總結如下;
先說思路:
在ListView的Adapter中,用一個Map保存每一項item的選擇狀態,在getView方法中,設置Map中保存的某一項的選擇狀態就實現了狀態的保存;
每一項的視圖child.xml
<CheckBox android:id="@+id/item_cb" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/item_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello" />
Adapter.java
public class CAdapter extends BaseAdapter { private List<String> list; private LayoutInflater inflater; Map<Integer, Boolean> map; private OnSelectedItemChanged listener; private Holder holder = null; public CAdapter(Context context, List<String> list, OnSelectedItemChanged listener) { super(); inflater = LayoutInflater.from(context); this.list = list; map = new HashMap<Integer, Boolean>(); for (int i = 0; i < list.size(); i++) { map.put(i, false); } this.listener = listener; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { View view = convertView; String item = list.get(position); if (view == null) { holder = new Holder(); view = inflater.inflate(R.layout.child, null); TextView tv = (TextView) view.findViewById(R.id.item_tv); CheckBox cb = (CheckBox) view.findViewById(R.id.item_cb); holder.tv = tv; holder.cb = cb; view.setTag(holder); } else { holder = (Holder) view.getTag(); holder.tv.setText(item); holder.cb.setChecked(map.get(position)); } holder.tv.setText(list.get(position)); final CheckBox cb = holder.cb; cb.setChecked(map.get(position));// 設置選擇狀態 cb.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { map.put(position, cb.isChecked()); listener.selectedItemChange(getSelectedCount(map)); } }); return view; } /** * 獲取選擇的項的數目 * * @param map * @return */ public int getSelectedCount(Map<Integer, Boolean> map) { int i = 0; for (Entry<Integer, Boolean> entry : map.entrySet()) { if (entry.getValue()) { i++; } } return i; } class Holder { TextView tv; CheckBox cb; } /** * 向Activity暴露選擇了多少項 * * @author cj * */ public interface OnSelectedItemChanged { public void selectedItemChange(int count); } public void selectAll() { // 全選 for (int i = 0; i < list.size(); i++) { map.put(i, true); } notifyDataSetChanged(); } public void disSelectAll() { // 全不選 for (int i = 0; i < list.size(); i++) { map.put(i, false); } notifyDataSetChanged(); } public void switchSelect() { // 反選 for (int i = 0; i < list.size(); i++) { boolean select = map.get(i); map.put(i, !select); } notifyDataSetChanged(); } }
MainActivity.java
public class MainActivity extends Activity { private ListView lv; private CAdapter adapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.lv); setAdapter(); } private void setAdapter() { List<String> list = new ArrayList<String>(); for (int i = 0; i < 50; i++) { list.add("item - " + i); } adapter = new CAdapter(getApplicationContext(), list, new OnSelectedItemChanged() {// Adapter接口暴露出來的選擇項狀態改變時選擇的項的數目 @Override public void selectedItemChange(int count) { Log.e("SelectedCount - ", count + ""); } }); lv.setAdapter(adapter); } // 全選 public void selectAll(View v) { adapter.selectAll(); } // 全不選 public void disSelectAll(View v) { adapter.disSelectAll(); } // 反選 public void switchSelect(View v) { adapter.switchSelect(); } }
選擇之后反選的效果(右邊是點擊反選之后的效果):
在上面用到一個比較好的思想就是用類的內部接口向外部調用類暴露本類的一些狀態改變時,外部類可能想要獲取的數據信息;
延伸思考:上面保存的狀態是CheckBox,當需要使用RadioButton的時候,使用方法也是類似的;
但是RadioButton可能會有另一種需求{選擇某一項的時候其它項就不選擇,也即只選擇一項},此種情況其實參考上面全選,反選的實現,實現起來也是比較簡單,暫時沒時間去寫出來測試;