首页 > 解决方案 > RecyclerView 不会在新搜索中自行清除

问题描述

我正在开发这个简单的 Github 用户搜索应用程序:

在此处输入图像描述

每次我进行新搜索时,都没有清除 recyclerview,而是将新的搜索结果简单地附加到末尾,这不是我想要的。我在进行新搜索之前明确清除了搜索适配器,但似乎不正确。

一些相关代码:

MainActivity.java

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_search, menu);

        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) searchItem.getActionView();
        searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                who = query;
                currentPage = 1;
                searchAdapter.clear();
                searchAdapter.notifyDataSetChanged();
                doSearchUser(query, currentPage);
                //searchAdapter.getFilter().filter(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });

        return true;
    }

SearchAdapter.java

package com.divbyzero.app.githubusersearch.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.divbyzero.app.githubusersearch.R;
import com.divbyzero.app.githubusersearch.model.User;

import java.util.ArrayList;
import java.util.List;

public class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchViewHolder> implements Filterable {
    private List<User> fullSearchResult;
    private List<User> filteredSearchResult;
    private List<User> theData;
    private Context ctxt;

    class SearchViewHolder extends RecyclerView.ViewHolder {
        ImageView github_profile_pic;
        TextView github_login;

       public SearchViewHolder(View itemView) {
            super(itemView);
            github_profile_pic = itemView.findViewById(R.id.github_profile_pic);
            github_login  = itemView.findViewById(R.id.github_login);
        }
    }

    public SearchAdapter(Context ctxt, List<User> list) {
        this.ctxt = ctxt;
        theData = list;
    }

    public void clear() {
        int size = theData.size();
        theData.clear();
        notifyItemRangeRemoved(0, size);
    }

    @NonNull
    @Override
    public SearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_item,
                parent, false);
        return new SearchViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull SearchViewHolder holder, int position) {
        User user = theData.get(position);
        Glide.with(ctxt)
                .load(user.getAvatarUrl())
                .override(200, 200)
                .into(holder.github_profile_pic);
        holder.github_login.setText(user.getLogin());
    }

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

    @Override
    public Filter getFilter() {
        return searchFilter;
    }

    private Filter searchFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            List<User> filteredList = new ArrayList<>();

            if (constraint == null || constraint.length() == 0) {
                filteredList.addAll(fullSearchResult);
            } else {
                String filterPattern = constraint.toString().toLowerCase().trim();

                for (User item : fullSearchResult) {
                    if (item.getLogin().toLowerCase().contains(filterPattern)) {
                        filteredList.add(item);
                    }
                }
            }

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

            return results;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            filteredSearchResult.clear();
            filteredSearchResult.addAll((List) results.values);
            notifyDataSetChanged();
        }
    };

    public void updateData(List<User> newList){
        this.theData.clear();
        this.theData.addAll(newList);
    }
}

完整代码: https ://github.com/anta40/GithubUserSearch

更新:

在加载更多数据时,我发现:无法在滚动回调中调用此方法。滚动回调可能会在您无法更改 RecyclerView 数据的度量和布局传递期间运行。任何可能改变 RecyclerView 结构或适配器内容的方法调用都应该推迟到下一帧

所以数据加载部分本身是不正确的。

如何解决这个问题?

标签: androidandroid-recyclerview

解决方案


有几个提示:

首先,适配器每次都返回完整列表的大小,但它应该返回过滤列表的大小;并且您需要在theData没有执行过滤时将过滤后的列表初始化为原始列表( )。

所以,改变这个:

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

至:

@Override
public int getItemCount() {
    return filteredSearchResult == null ? 0 : filteredSearchResult.size();
}

并在一开始就初始化filteredSearchResult(在适配器构造函数中)

public SearchAdapter(Context ctxt, List<User> list) {
    this.ctxt = ctxt;
    theData = list;
    filteredSearchResult = list; // <<<<<<<<< here is the change
}

其次,您从原始列表中onBindViewHolder()获取user(不是过滤列表,因此很可能从未过滤列表中返回错误数据);所以也将其更改为过滤列表。

所以替换theDatafilteredSearchResult

@Override
public void onBindViewHolder(@NonNull SearchViewHolder holder, int position) {
    User user = filteredSearchResult.get(position); // <<<<<<<<< here is the change
    ...
}

每当您清除/设置原始列表时,更改过滤列表

public void clear() {
    int size = theData.size();
    theData.clear();
    filteredSearchResult.clear(); // <<<<<<<<< here is the change
    notifyItemRangeRemoved(0, size);
}


public void updateData(List<User> newList){
    this.theData.clear();
    this.theData.addAll(newList);
    filteredSearchResult.addAll(newList);  // <<<<<<<<< here is the change
}

推荐阅读