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

Android开发中置于RecyclerView内的SearchView搜索过滤功能失效问题

问题根因排查
  • 过滤逻辑初始化错误:原代码在performFiltering中先把当前已展示的data集合拷贝到过滤列表中,而非创建空列表。导致搜索时会把上一次残留的不匹配数据一并保留在结果中,关键词清空时还会出现全量数据和残留数据重复的问题,完全无法实现正确的过滤效果。
  • 集合引用绑定错误:构造方法中直接将外部传入的data集合赋值给适配器内部变量,没有做独立拷贝,容易出现外部修改集合时,适配器内部数据和全量备份集合不同步的问题。
  • 多ViewType适配缺失:将SearchView作为RecyclerView内部item放置时,原适配器没有做布局类型区分,会直接把SearchView布局当成普通数据item绑定,触发类型转换异常、布局错乱的运行问题,也无法正确捕获SearchView的输入回调。
修复后的完整实现
public class SearchAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
    // 定义布局类型常量
    private static final int VIEW_TYPE_SEARCH_HEADER = 0;
    private static final int VIEW_TYPE_DATA_ITEM = 1;

    private Context context;
    // 当前展示的搜索结果数据集
    private ArrayList<String> showDataList = new ArrayList<>();
    // 全量原始备份数据集
    private ArrayList<String> fullDataList = new ArrayList<>();

    public SearchAdapter(Context context, ArrayList<String> data) {
        this.context = context;
        // 对内部维护的集合做独立拷贝,解耦外部传入的集合引用
        this.showDataList = new ArrayList<>(data);
        this.fullDataList = new ArrayList<>(data);
    }

    @Override
    public int getItemViewType(int position) {
        // 列表第一个位置固定为SearchView头部布局
        if (position == 0) {
            return VIEW_TYPE_SEARCH_HEADER;
        }
        return VIEW_TYPE_DATA_ITEM;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_SEARCH_HEADER) {
            // 加载包含SearchView的头布局
            View headerView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_search_header, parent, false);
            return new SearchHeaderViewHolder(headerView);
        }
        // 加载普通数据项布局
        View dataView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycledata, parent, false);
        return new DataItemViewHolder(dataView);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        int currentViewType = getItemViewType(holder.getAdapterPosition());
        if (currentViewType == VIEW_TYPE_SEARCH_HEADER) {
            SearchHeaderViewHolder headerHolder = (SearchHeaderViewHolder) holder;
            // 直接绑定SearchView的输入回调,自动触发过滤
            headerHolder.searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    return false;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    getFilter().filter(newText);
                    return true;
                }
            });
            return;
        }
        // 绑定普通数据项,因头部占位1个位置,需对索引做偏移
        DataItemViewHolder dataHolder = (DataItemViewHolder) holder;
        String itemContent = showDataList.get(position - 1);
        dataHolder.textView.setText(itemContent);
    }

    @Override
    public int getItemCount() {
        // 总条目数 = 1个搜索头 + 当前展示的数据条数
        return showDataList.size() + 1;
    }

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                // 初始化空的过滤结果列表,不提前拷贝当前展示数据
                ArrayList<String> filterResultList = new ArrayList<>();
                String keyword = constraint.toString().toLowerCase().trim();
                if (keyword.isEmpty()) {
                    // 关键词为空时,直接加载全量数据
                    filterResultList.addAll(fullDataList);
                } else {
                    // 遍历全量数据匹配关键词,仅将符合条件的条目加入结果
                    for (String item : fullDataList) {
                        if (item.toLowerCase().contains(keyword)) {
                            filterResultList.add(item);
                        }
                    }
                }
                FilterResults results = new FilterResults();
                results.values = filterResultList;
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                showDataList.clear();
                showDataList.addAll((ArrayList<String>) results.values);
                notifyDataSetChanged();
            }
        };
    }

    // 搜索头布局ViewHolder
    class SearchHeaderViewHolder extends RecyclerView.ViewHolder {
        SearchView searchView;
        public SearchHeaderViewHolder(@NonNull View itemView) {
            super(itemView);
            searchView = itemView.findViewById(R.id.search_view);
        }
    }

    // 普通数据项ViewHolder
    class DataItemViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        public DataItemViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textview);
        }
    }
}
关键修复说明
  • 重构过滤核心逻辑:过滤结果从空集合开始构建,完全基于全量备份数据做关键词匹配,彻底解决旧数据残留、重复展示的问题。
  • 解决集合引用不同步问题:适配器内部维护的展示数据、全量备份数据均做独立内存拷贝,和外部传入的集合解耦,避免集合修改导致的状态异常。
  • 适配SearchView内嵌场景:通过多ViewType区分头部搜索布局和普通数据项,解决布局绑定的类型转换异常;同时直接在搜索头绑定逻辑中监听输入变化,自动触发过滤流程,无需外部手动调用。
  • 修正位置偏移问题:针对头部布局的占位场景,绑定普通数据时对item位置做偏移处理,避免索引越界、数据错绑的问题。

内容的提问来源于stack exchange,提问作者Muhammad Waseem Haider

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 09:57:03