You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView过滤后滑动ViewHolder获取Item位置异常问题

RecyclerView过滤后滑动Item,getAdapterPosition()返回原列表位置的解决办法

我来帮你解决这个问题!这个坑我之前也踩过,核心原因是你的Adapter在处理位置时混淆了原数据列表和过滤后的列表,导致滑动时拿到的是原列表的索引,而不是当前显示的过滤列表的位置。

先再明确下你的问题场景:
原列表是:

  • 0: Apple
  • 1: Banana
  • 2: Mango
  • 3: Coconut
  • 4: Peanut

搜索"Peanut"后,它显示在列表第一位,但滑动这个Item时,viewHolder.getAdapterPosition()返回的却是0(原Apple的位置)。

问题根源

你的Adapter大概率只维护了一个原数据列表,或者在处理滑动事件时,错误地用getAdapterPosition()去对应原列表的索引。而Filter过滤后,RecyclerView显示的是过滤后的子集,但getAdapterPosition()返回的是当前Adapter数据集(也就是过滤后的列表)中的位置,你如果直接用这个位置去取原列表的元素,自然就错了。

具体解决方案

1. 给Adapter维护两个列表

首先,你的Adapter里必须同时保存原数据列表和过滤后的列表:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private List<String> originalList; // 存储原始所有数据
    private List<String> filteredList; // 存储过滤后要显示的数据

    // 构造方法初始化原列表,同时把过滤列表初始化为原列表
    public FruitAdapter(List<String> originalList) {
        this.originalList = new ArrayList<>(originalList);
        this.filteredList = new ArrayList<>(originalList);
    }

    // ... 其他ViewHolder相关代码
}

2. 正确实现Filter类

确保Filter在过滤后正确更新filteredList,并通知Adapter刷新:

private class FruitFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults results = new FilterResults();
        List<String> tempFilteredList = new ArrayList<>();

        if (TextUtils.isEmpty(constraint)) {
            // 没有过滤条件,显示全部原数据
            tempFilteredList.addAll(originalList);
        } else {
            String filterText = constraint.toString().toLowerCase().trim();
            for (String fruit : originalList) {
                if (fruit.toLowerCase().contains(filterText)) {
                    tempFilteredList.add(fruit);
                }
            }
        }

        results.values = tempFilteredList;
        results.count = tempFilteredList.size();
        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        filteredList.clear();
        filteredList.addAll((List<String>) results.values);
        notifyDataSetChanged(); // 通知Adapter更新UI
    }
}

3. 修正滑动事件中的位置处理

在onSwiped方法中,getAdapterPosition()返回的是过滤后列表的位置,你需要用这个位置去获取过滤后的Item,再根据Item找到原列表中的位置(如果需要的话):

@Override 
public void onSwiped(RecyclerView.ViewHolder viewHolder, final int direction) { 
    int filteredPos = viewHolder.getAdapterPosition();
    if (filteredPos == RecyclerView.NO_POSITION) return; // 防止无效位置

    // 获取当前显示的Item(过滤后的)
    String currentFruit = filteredList.get(filteredPos);

    // 如果需要原列表中的位置,这样获取
    int originalPos = originalList.indexOf(currentFruit);
    Log.e("ORIGINAL_POSITION", "原列表位置:" + originalPos);
    Log.e("FILTERED_POSITION", "过滤后列表位置:" + filteredPos);

    // 后续处理逻辑,比如删除操作:如果是删除过滤后的Item,要同时更新原列表和过滤列表
    originalList.remove(currentFruit);
    filteredList.remove(filteredPos);
    notifyItemRemoved(filteredPos);
}

4. 绑定ViewHolder时用过滤后的列表

别忘了在onBindViewHolder中,也是从filteredList取数据,而不是原列表:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    String fruit = filteredList.get(position);
    holder.fruitNameTv.setText(fruit);
}

@Override
public int getItemCount() {
    return filteredList.size();
}

额外提醒

如果你的数据是复杂对象(不是简单字符串),建议在对象中添加一个字段存储原列表的位置,避免用indexOf可能带来的重复数据问题:

public class Fruit {
    private String name;
    private int originalPosition; // 存储原列表中的索引

    // 构造方法、getter、setter
}

这样在过滤时,每个Item都保留了原位置,滑动时直接filteredList.get(filteredPos).getOriginalPosition()就能拿到原索引,更高效也更准确。

内容的提问来源于stack exchange,提问作者IAmNotARobot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:35:31