SearchView过滤RecyclerView 点击位置错位、数据不匹配
问题核心根源
- 数据来源错位:Adapter过滤后内部维护的是临时的过滤结果列表,但MainActivity处理点击事件时,直接从自身持有的原始全量列表
mCategories中按position取数据,两个列表的元素、索引完全不对应。举个实际复现场景:原始列表为[分类A, 分类B, 分类C, 分类D],搜索关键词“分类B”后,Adapter实际展示的列表为[分类B]。此时用户点击这个展示的条目(Adapter返回的位置是0),原代码会从全量列表取位置0的元素,也就是分类A,和用户实际看到的点击项完全不符,这就是错位的直接表现。
- 过滤实现不规范:
performFiltering运行在子线程,原代码直接在子线程修改Adapter的主线程成员变量mCategorie,容易引发并发导致的列表异常、数据错乱。 - 初始化逻辑冗余:MainActivity在解析JSON的循环内每加一个条目就重新创建一次Adapter,不仅造成性能浪费,也容易导致Adapter持有不同步的列表引用。
具体修复步骤
1. 修正点击事件的数据获取逻辑
Adapter已经封装了getItem(int position)方法,该方法从Adapter当前正在展示的列表(不管是过滤后还是原始列表)中取对应位置的数据,直接替换MainActivity中原先从全量列表取数据的逻辑:
@Override public void onItemClick(View view, int position) { Intent intent = new Intent(MainActivity.this, TagsActivity.class); // 从Adapter当前维护的展示列表中获取被点击的实体,而非直接读原全量列表 Categorie clickedItem = (Categorie) adapter.getItem(position); intent.putExtra("id_cat", clickedItem.getId()); intent.putExtra("title", clickedItem.getName()); startActivityForResult(intent, 1); }
2. 优化Filter实现,避免子线程修改UI成员
原代码在performFiltering的子线程中直接给Adapter的mCategorie赋值,存在并发风险。调整为子线程只操作本地变量,所有列表更新逻辑放到主线程的publishResults方法中执行:
private Filter exampleFilter = new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { // 子线程内创建独立的过滤结果列表,不修改Adapter全局成员 List<Categorie> filteredResult = new ArrayList<>(); String filterKeyword = constraint.toString().toLowerCase().trim(); if (filterKeyword.isEmpty()) { filteredResult.addAll(mCategoriefull); } else { for (Categorie item : mCategoriefull) { if (item.getName().toLowerCase().contains(filterKeyword)) { filteredResult.add(item); } } } FilterResults results = new FilterResults(); results.values = filteredResult; return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { // 主线程内更新Adapter的展示列表,刷新UI mCategorie = (List<Categorie>) results.values; notifyDataSetChanged(); } };
3. 修正Adapter的列表拷贝逻辑,避免引用错乱
原Adapter构造函数直接把传入的列表引用赋值给全量备份mCategoriefull,如果外部修改原列表,会直接导致过滤源被篡改。调整为对列表做一次拷贝:
public CategorieAdapter(Context mContext, List<Categorie> mCategorie) { this.mContext = mContext; // 展示列表独立拷贝,避免外部引用变更影响Adapter内部状态 this.mCategorie = new ArrayList<>(mCategorie); // 全量备份列表独立拷贝,保证过滤操作的数据源稳定 this.mCategoriefull = new ArrayList<>(mCategorie); this.mInflater = LayoutInflater.from(mContext); }
4. 优化MainActivity的数据加载逻辑
原代码在JSON解析的循环内反复创建Adapter、绑定RecyclerView,调整为等所有数据加载完成后,一次性初始化Adapter:
@Override public void onResponse(String response) { try { JSONArray jsonArray = new JSONArray(response); mCategories.clear(); // 清空原有缓存,避免重复加载 for (int i = 0; i < jsonArray.length(); i++) { JSONObject object = jsonArray.getJSONObject(i); int id = Integer.parseInt(object.getString("id_cat")); String name = object.getString("name"); mCategories.add(new Categorie(id, name)); } // 全部数据解析完成后,再初始化并绑定Adapter adapter = new CategorieAdapter(MainActivity.this, mCategories); categoriesListView.setAdapter(adapter); adapter.setClickListener(this); } catch (JSONException e) { e.printStackTrace(); } }
内容的提问来源于stack exchange,提问作者samg
相关产品推荐
相关产品推荐

