前段时间要做一个批量删除的功能,身为初学者的我,开始尝试做这个功能,但是出现了以下3个情况:
- checkbox被勾选上后,listview滑走再滑回来,本来应该被选中的checkbox成了未选中状态
- 连续勾选后,删除的不完整,比如我同时勾上了第3、4、5个item,点击删除后第3、5个倒是被删除了,第4个没有被删除
- 乱序,明明选择的是第2个,点击删除的时候,随机的一个被删除了
本来在网上查了一些解决的方法,但是都太复杂了(个人感觉),一个简单的批量删除,居然用map这么高级的东西,所以我分享一下我解决的方法。
先上效果图:
界面超级简单,完全是为了做批量删除而的一个小例子,也没有夹杂一些别的东西,比如全选反选之类的,我喜欢一篇文章只说一个问题,
使用到的类
- MainActivity:不解释
- MyAdapter:listview的适配器
- Entity:实体类,封装了listview的item的内容
接下来一个一个解释,每个类到底有什么东西
先说最简的Entity类
代表着每一个item的内容
//是否被选中
private boolean isSelect;
//文本内容
private String text;
就两个属性,别的都是一些get、set方法,tostring什么的。
接下来是MyAdapter类:
继承BaseAdapter,实现对应的4个方法
以下是继承BaseAdapter的常见手段(方法)
private ArrayList<Entity> datas = new ArrayList<>();
private LayoutInflater inflater;
public MyAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
public void setList(ArrayList<Entity> list) {
this.datas = list;
}
现在说说最重要的getView方法
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//使用viewholder优化listview
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//将position传递给checkbox监听的实现类
//说白了就是让chechbox知道它在listview的哪个位置
//好处就是,删除的时候不会乱了,不会因为选中的是a而把b删了(解决乱序)
//因为checkbox知道自己的位置了嘛
holder.checkbox.setTag(position);
holder.text.setText(datas.get(position).getText());
//给checkbox设置监听
//使他被选中的时候做一些事情
//比如将对应的entity的isSelect改为true
holder.checkbox.setOnCheckedChangeListener(listener);
//给checkbox设置状态,是否被选中
//通过获取对应的entity,取得里面的isSelect的值修改
//解决选中后,listview滑动走再滑回来,checkbox又没有选的尴尬
holder.checkbox.setChecked(datas.get(position).isSelect());
return convertView;
}
private CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//获取传递来的position,点击listview列表中的checkbox的时候,就能知道是点击的哪个了
int pos = (int) buttonView.getTag();
//将集合中对应的entity中对应的isSelect设置为checkbox的状态(是否被选中true/false)
getItem(pos).setSelect(isChecked);
}
};
public static class ViewHolder {
public View rootView;
public TextView text;
public CheckBox checkbox;
public ViewHolder(View rootView) {
this.rootView = rootView;
this.text = (TextView) rootView.findViewById(R.id.text);
this.checkbox = (CheckBox) rootView.findViewById(R.id.checkbox);
}
}
准备工作都做完了,我们来看看MainActivity中的具体实现
布局很简单,线性布局,方向垂直,上面一个批量删除的按钮,下面是listview。
先来感受一下MainActivity里面的内容:
public class MainActivity extends AppCompatActivity {
private ListView listview;
private Button del;
private MyAdapter adapter;
private ArrayList<Entity> datas;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
datas = new ArrayList<>();
for (int i = 0; i < 30; i++) {
datas.add(new Entity(false, "item" + i));
}
listview = (ListView) findViewById(R.id.listview);
del = (Button) findViewById(R.id.del);
adapter = new MyAdapter(this);
adapter.addData(datas);
listview.setAdapter(adapter);
del.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList<Entity> data = adapter.getData();
for (int i = 0; i < data.size(); i++) {
if (data.get(i).isSelect()) {
data.remove(i);
i--;
}
}
adapter.setList(data);
adapter.notifyDataSetChanged();
}
});
}
}
别的都没上什么说的,我们来看看点击事件里面有个i - -,这是干嘛的呢,为什么要这么写呢,不写会有什么后果呢?
不写的后果很严重,就是连续勾选item后,点击删除,不会全部被删除,为什么会这样,这要牵扯到ArrayList的结构。大家都知道ArrayList是类似数组的结构,所以,
来看一张我用画图工具画的渣图
该图表示arraylist中有很多数据,其中梨子的位置是1,香蕉的位置是2,当我们删除了梨子后,本来应该第2的香蕉,变成的位置1,也就是梨子后面所有的数据都想前挪动了一位(感觉好耗资源啊)
所以当我们连续选择又没有i- -时候,画图最清楚,上图!
这里解释下:
图1:当我们选中234的时候,实际上是将每一个item对应的entity中的isSelect值改为了true。
图2:点击删除后,系统开始循环遍历整个集合,为true就删除,该循环只执行一次,
所以当循环到了位置2的时候,发现是true,不解释直接删除,此时集合中的数据位置为图2,
位置2已被item3代替
图3:继续循环,到了位置3,发现是item4,一看值为true,直接删,此时集合中的数据位置为图3
所以最终结果就是item3瞒天过海了。。。
结论
在for循环中,我们指定了循环的次数,int times = date.size(),但是当我们删除了集合中的一个数据后,times已经发生改变了,为了不使他改变,删除一个就i- -一次,这样,就能保证循环的次数和集合尺寸一样了,就能完美实现批量删除了。