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

基于ViewModel LiveData的RecyclerView添加SearchView过滤失败求助

解决ViewModel+LiveData下RecyclerView的SearchView过滤问题

看起来你遇到的核心问题是Adapter里的数据源逻辑搞反了,再加上ViewModel的LiveData更新会重置数据,导致Filterable接口的过滤效果完全没体现出来。我帮你梳理下问题点,然后给出完整的修正方案:

问题根源分析

  1. 你的getItemCount()和onBindViewHolder()里用的都是原始数据源mNetworkEntries,而过滤后的mFilteredNetworkEntries根本没被用来展示数据——这就导致不管过滤逻辑怎么写,界面显示的还是原始列表,自然看不到过滤效果。
  2. setNetworks()方法里用mFilteredNetworkEntries.addAll(mNetworkEntries)会导致重复添加数据,当ViewModel的LiveData更新时,原始数据被替换,但过滤列表会累积旧数据,出现混乱。
  3. 没有正确关联SearchView和Adapter的过滤逻辑,或者LiveData更新时没有保留当前的搜索状态。

修正方案

第一步:修复Adapter的数据源逻辑

把Adapter里用来展示的数据源换成mFilteredNetworkEntries,原始数据mNetworkEntries只用来保存完整的列表,作为过滤的基准。修改后的关键代码如下:

public class NetworkAdapter extends RecyclerView.Adapter<NetworkAdapter.NetworksViewHolder> implements Filterable {
    // 成员变量保持不变,注意mFilteredNetworkEntries初始化空列表
    private List<NetworkEntry> mNetworkEntries;
    private List<NetworkEntry> mFilteredNetworkEntries = new ArrayList<>();
    private Context mContext;
    private ItemClickListener mItemClickListener;

    // 构造函数不变
    public NetworkAdapter(Context context, ItemClickListener listener) {
        mContext = context;
        mItemClickListener = listener;
    }

    @Override
    public NetworksViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext)
                .inflate(R.layout.network_list_item, parent, false);
        return new NetworksViewHolder(view);
    }

    @Override
    public void onBindViewHolder(NetworksViewHolder holder, int position) {
        holder.onBindData(position);
    }

    // 1. 修改getItemCount,返回过滤后的列表长度
    @Override
    public int getItemCount() {
        return mFilteredNetworkEntries.size();
    }

    // 2. 修改setNetworks方法,正确初始化原始数据和过滤列表
    public void setNetworks(List<NetworkEntry> networkEntries) {
        mNetworkEntries = new ArrayList<>(networkEntries); // 保存原始数据副本
        mFilteredNetworkEntries.clear();
        mFilteredNetworkEntries.addAll(networkEntries); // 初始化过滤列表为完整数据
        notifyDataSetChanged();
    }

    // 3. 修改ViewHolder的onBindData,从过滤列表取数据
    public class NetworksViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        ImageView networkLogoImageView;
        TextView networkNameTextView;
        TextView joinTextView;
        TextView commissionTextView;
        TextView minPayTextView;
        TextView payFrequencyTextView;
        TextView offerTextView;
        TextView likeTextView;
        View containerView;

        public NetworksViewHolder(View itemView) {
            super(itemView);
            // 控件初始化逻辑不变
            networkLogoImageView = itemView.findViewById(R.id.iv_logo);
            networkNameTextView = itemView.findViewById(R.id.tv_network_name);
            joinTextView = itemView.findViewById(R.id.bt_join);
            commissionTextView = itemView.findViewById(R.id.iv_commission);
            minPayTextView = itemView.findViewById(R.id.tv_min_pay);
            payFrequencyTextView = itemView.findViewById(R.id.tv_pay_frequency);
            offerTextView = itemView.findViewById(R.id.iv_offers);
            likeTextView = itemView.findViewById(R.id.iv_likes);
            containerView = itemView.findViewById(R.id.layoutContainer);
            
            joinTextView.setOnClickListener(this);
            containerView.setOnClickListener(this);
        }

        public void onBindData(int position) {
            // 关键:从过滤后的列表获取数据
            NetworkEntry networkEntry = mFilteredNetworkEntries.get(getAdapterPosition());
            Picasso.with(mContext).load(networkEntry.getNetworkImageUrl()).into(networkLogoImageView);
            networkNameTextView.setText(networkEntry.getNetworkName());
            commissionTextView.setText(networkEntry.getNetworkCommission());
            minPayTextView.setText(mContext.getString(R.string.tv_min_pay, networkEntry.getNetworkMinPay()));
            payFrequencyTextView.setText(networkEntry.getNetworkPayFrequency());
            // 补全剩余控件的绑定逻辑
            offerTextView.setText(networkEntry.getNetworkOffers());
            likeTextView.setText(networkEntry.getNetworkLikes());
        }

        @Override
        public void onClick(View v) {
            // 点击逻辑不变,注意从过滤列表取数据
            NetworkEntry entry = mFilteredNetworkEntries.get(getAdapterPosition());
            mItemClickListener.onItemClickListener(entry.getNetworkData());
        }
    }

    // 4. 优化Filter逻辑,避免空指针
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String searchQuery = charSequence.toString().trim().toLowerCase();
                List<NetworkEntry> filteredList = new ArrayList<>();

                if (TextUtils.isEmpty(searchQuery) || mNetworkEntries == null) {
                    filteredList.addAll(mNetworkEntries != null ? mNetworkEntries : Collections.emptyList());
                } else {
                    for (NetworkEntry entry : mNetworkEntries) {
                        // 这里可以扩展多字段过滤,比如同时匹配佣金、支付频率等
                        if (entry.getNetworkName().toLowerCase().contains(searchQuery)) {
                            filteredList.add(entry);
                        }
                    }
                }

                FilterResults results = new FilterResults();
                results.values = filteredList;
                return results;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                mFilteredNetworkEntries = (List<NetworkEntry>) filterResults.values;
                notifyDataSetChanged();
            }
        };
    }

    public interface ItemClickListener {
        void onItemClickListener(NetworkData networkData);
    }
}

第二步:在Activity/Fragment中关联SearchView和ViewModel

在你的界面类里,需要同时处理LiveData的数据更新和SearchView的过滤触发,还要保证LiveData更新时保留当前的搜索状态:

public class NetworkListFragment extends Fragment {
    private NetworkAdapter mAdapter;
    private NetworkViewModel mViewModel;
    private SearchView mSearchView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_network_list, container, false);

        // 初始化RecyclerView
        RecyclerView recyclerView = view.findViewById(R.id.recycler_view_networks);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        mAdapter = new NetworkAdapter(getContext(), this::onNetworkItemClick);
        recyclerView.setAdapter(mAdapter);

        // 初始化SearchView
        mSearchView = view.findViewById(R.id.search_view_networks);
        mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                mAdapter.getFilter().filter(query);
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                mAdapter.getFilter().filter(newText);
                return true;
            }
        });

        // 初始化ViewModel并观察LiveData
        mViewModel = new ViewModelProvider(this).get(NetworkViewModel.class);
        mViewModel.getNetworkEntries().observe(getViewLifecycleOwner(), networkEntries -> {
            mAdapter.setNetworks(networkEntries);
            // 如果当前有搜索关键词,更新数据后重新过滤
            String currentQuery = mSearchView.getQuery().toString();
            if (!TextUtils.isEmpty(currentQuery)) {
                mAdapter.getFilter().filter(currentQuery);
            }
        });

        return view;
    }

    private void onNetworkItemClick(NetworkData networkData) {
        // 处理条目点击逻辑
    }
}

额外注意事项

  • 确保NetworkEntry类是不可变的,或者在ViewModel中返回新的列表实例,这样LiveData才能正确触发观察者回调。
  • 如果数据量很大,过滤逻辑可以进一步优化,比如使用流式API或者提前缓存索引。
  • Filterable接口的performFiltering()方法本身是在后台线程执行的,不用担心阻塞主线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:06:54