自定义Adapter实现Filter的ListView触发IndexOutOfBoundsException问题
解决ListView搜索过滤异常的问题
嘿,我看你遇到了自定义ListView搜索过滤失效的问题,结合你给出的代码片段,大概率是Filter实现里的数据处理逻辑踩了坑——毕竟用ArrayAdapter做过滤时,很容易因为没区分「原始完整数据集」和「过滤后数据集」导致异常,咱们一步步来修复:
首先,排查核心问题
你当前的Adapter里可能只维护了一个warrantyList,过滤操作直接修改了这个列表,导致后续无法恢复原始数据,或者过滤逻辑没有正确更新UI展示的数据。这是新手实现Filter最常见的错误。
正确的Adapter实现方案
我们需要在Adapter中维护两个数据集:
- 一个保存从数据库获取的完整原始数据(用于过滤时的匹配源)
- 一个保存当前要展示的过滤后数据(用于ListView的显示)
下面是完整的修正版Adapter代码:
public class WarrantyAdapter extends ArrayAdapter<ServicesWarranty> implements Filterable { // 原始完整数据集,过滤时的匹配源 private ArrayList<ServicesWarranty> originalWarranties; // 过滤后要展示的数据集 private ArrayList<ServicesWarranty> filteredWarranties; private Context context; public WarrantyAdapter(Context context, ArrayList<ServicesWarranty> warranties) { super(context, 0, warranties); this.context = context; // 初始化原始数据集(做深拷贝,避免引用传递导致原始数据被修改) this.originalWarranties = new ArrayList<>(warranties); this.filteredWarranties = new ArrayList<>(warranties); } @Override public int getCount() { // 返回过滤后数据集的数量 return filteredWarranties.size(); } @Override public ServicesWarranty getItem(int position) { // 返回过滤后数据集对应位置的项 return filteredWarranties.get(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { // 这里是你原本的getView实现,注意用filteredWarranties.get(position)获取数据 ServicesWarranty warranty = filteredWarranties.get(position); // ... 你的视图绑定逻辑(比如填充文本、图片等) return convertView; } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); ArrayList<ServicesWarranty> tempFilteredList = new ArrayList<>(); // 如果搜索关键词为空,直接返回原始数据集 if (constraint == null || constraint.length() == 0) { tempFilteredList.addAll(originalWarranties); } else { String filterPattern = constraint.toString().toLowerCase().trim(); // 遍历原始数据集,匹配关键词 for (ServicesWarranty item : originalWarranties) { // 替换成你实际要匹配的字段,比如item.getWarrantyNo()、item.getCustomerName()等 if (item.getTargetField().toLowerCase().contains(filterPattern)) { tempFilteredList.add(item); } } } results.values = tempFilteredList; results.count = tempFilteredList.size(); return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { // 更新过滤后数据集,并通知ListView刷新 filteredWarranties.clear(); filteredWarranties.addAll((ArrayList<ServicesWarranty>) results.values); notifyDataSetChanged(); } }; } }
然后修正你的listShow方法
确保初始化Adapter时传入完整的数据集,并且不需要每次过滤都重新初始化Adapter:
private void listShow() { warranties = db.getAllServWarr(); // 初始化Adapter时传入完整的原始数据 adapter = new WarrantyAdapter(serviceswarranty_activity.this, warranties); listView.setAdapter(adapter); }
最后,搜索触发的逻辑示例
比如你在搜索框的文本变化监听里调用过滤:
searchEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // 调用Adapter的过滤方法 adapter.getFilter().filter(s); } @Override public void afterTextChanged(Editable s) {} });
关键注意点
- 深拷贝数据集:初始化
originalWarranties时要用new ArrayList<>(warranties),避免和传入的列表共用同一个引用,导致原始数据被意外修改。 - 过滤逻辑要匹配实际字段:把
item.getTargetField()替换成你实际要搜索的字段(比如保修单号、客户名称等)。 - performFiltering是后台线程:不要在这个方法里操作UI,所有UI更新都要放在
publishResults里。
内容的提问来源于stack exchange,提问作者BeginnerProgrammer
相关产品推荐
相关产品推荐

