最近在一个项目中,需要在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可能会有另一种需求{选择某一项的时候其它项就不选择,也即只选择一项},此种情况其实参考上面全选,反选的实现,实现起来也是比较简单,暂时没时间去写出来测试;