如何使用RecyclerView实施搜索过滤器

问题描述

我遵循了一些有关sqlite和RecyclerView(https://www.youtube.com/watch?v=VQKq9RHMS_0&ab_channel=Stevdza-San)的youtube教程,我完成了所有工作,并根据需要对我的应用程序进行了自定义,一切正常。

现在我要做的就是在顶部(操作栏)添加搜索按钮,以便我可以从RecyclerView中搜索项目。

因此,我试图弄清楚如何实现它,但是我在过滤器和所有这些方面都感到吃力。在CustomeAdpter获得ArrayList的同时,我看到的每个与List(E)一起使用的教程都无法理解如何使其与ArrayList一起使用。

所以,如果有人可以帮助我并给我一些指导,我将不胜感激

编辑

我在适配器中添加getFilter()函数,但是当我尝试搜索RecyclerView时什么也没发生。

我正在添加我的代码:MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    activity = MainActivity.this;
    noClubsImage = findViewById(R.id.noClubsImage);
    noClubsText = findViewById(R.id.noClubsText);
    recyclerView = findViewById(R.id.recyclerView);
    add_button = findViewById(R.id.add_button);

    add_button.setonClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this,AddActivity.class);
            activity.startActivityForResult(intent,1);
        }
    });

    myDB = new MyDatabaseHelper(MainActivity.this);
    club_id = new ArrayList<>();
    club_name = new ArrayList<>();
    join_date = new ArrayList<>();
    expire_date = new ArrayList<>();

    storeDataInArrays();


    customAdapter = new CustomAdapter(MainActivity.this,this,club_id,club_name,join_date,expire_date);
    recyclerView.setAdapter(customAdapter);
    recyclerView.setLayoutManager(new linearlayoutmanager(MainActivity.this));
}

。 。 。 编辑 ***

    @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_menu,menu);
    MenuItem item = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) item.getActionView();
    searchView.setonQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            customAdapter.getFilter().filter(query);
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            customAdapter.getFilter().filter(newText);
            return true;
        }
    });
    return super.onCreateOptionsMenu(menu);

}
}

这是我的适配器:

我在onBindViewHolder中添加了Try&catch函数,因为它会崩溃,并得到以下错误:“ java.lang.indexoutofboundsexception:Index:1,Size:1” 添加后,我可以搜索但很奇怪的东西,上传一些图片When I start the app Start searching "Yasha" -> it change the two rows

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> implements Filterable{


private Context context;
private Activity activity;
private ArrayList club_id,expire_date,list,originalList;
int position;
Animation translate_anim;


CustomAdapter(Activity activity,Context context,ArrayList club_id,ArrayList club_name,ArrayList join_date,ArrayList expire_date){


    this.activity = activity;
    this.context = context;
    this.club_id = club_id;
    this.club_name = club_name;
    this.join_date = join_date;
    this.expire_date = expire_date;

    this.list = club_name;
    this.originalList = club_name;

}



@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) {
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = inflater.inflate(R.layout.my_row,parent,false);
    return new MyViewHolder(view);
}

public Filter getFilter(){
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            ArrayList filteredResults = null;
            if (constraint.length() == 0) {
                filteredResults = club_name;
            } else {
                filteredResults = getFilteredResults(constraint.toString().toLowerCase());
            }

            FilterResults results = new FilterResults();
            results.values = filteredResults;
            Log.d("Test",filteredResults.toString());
            return results;
        }

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

protected ArrayList getFilteredResults(String constraint) {
    ArrayList results = new ArrayList<>();
    for (Object item : originalList) {
        if (item.toString().toLowerCase().contains(constraint)) {
            results.add(item.toString());
        }
    }
    //Log.d("Test",results.toString());
    return results;
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder,final int position) {

    try{
        this.position = position;
        holder.club_name_txt.setText(String.valueOf(club_name.get(position)));
        holder.join_txt.setText(String.valueOf(join_date.get(position)));
        holder.expire_txt.setText(String.valueOf(expire_date.get(position)));
        holder.mainLayout.setonClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context,UpdateActivity.class);
                intent.putExtra("club_id",String.valueOf(club_id.get(position)));
                intent.putExtra("club_name",String.valueOf(club_name.get(position)));
                intent.putExtra("join_date",String.valueOf(join_date.get(position)));
                intent.putExtra("expire_date",String.valueOf(expire_date.get(position)));
                activity.startActivityForResult(intent,1);

            }
        });
    }catch(Exception ex) {
        Log.e("TAG","EXCEPTION CAUGHT WHILE EXECUTING DATABASE TRANSACTION");
        ex.printstacktrace();
    }
}

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

public class MyViewHolder extends RecyclerView.ViewHolder {

    LinearLayout mainLayout;
    TextView club_id_txt,club_name_txt,join_txt,expire_txt;

    public MyViewHolder(@NonNull View itemView) {
        super(itemView);

        club_name_txt = itemView.findViewById(R.id.club_name_txt);
        join_txt = itemView.findViewById(R.id.jDate_txt);
        expire_txt = itemView.findViewById(R.id.eDate_txt);
        mainLayout = itemView.findViewById(R.id.mainLayout);
        translate_anim = AnimationUtils.loadAnimation(context,R.anim.translate_anim);
        mainLayout.setAnimation(translate_anim);
    }
 }}

解决方法

customAdapter.getFilter();的两种方法中都有OnQueryTextListener行,它为您返回过滤器。这就是您所请求的所有内容,您从未向此过滤器传递过String。这条线应该看起来像这样

customAdapter.getFilter().filter(query); // or newText,depend on OnQueryTextListener method

根据评论进行编辑:

删除此try{}catch()并解决您的异常。这种方法是可靠的,只有您的错误才能导致崩溃。

您仅过滤和发布了一个数组(club_name = (ArrayList<String>) results.values;),但仍在通知adapter列表中的项数等于club_id.size();中的getItemCount。这就是为什么您获得IndexOutOfBoundsException-club_id并保持多个项目的原因,即使club_name里面只有很少或一个项目时也进行了过滤。您应该过滤所有四个数组,实际上这应该是一个带有所有四个参数的自定义对象的数组

一些修复教程:

将您的数据保留在一些其他数组中,这些数组将保持不变,即使过滤后也保留所有数据。首先创建“原始”和“工作”数组

CustomAdapter(Activity activity,Context context,ArrayList club_id,ArrayList club_name,ArrayList join_date,ArrayList expire_date){
    this.activity = activity;
    this.context = context;

    this.club_id_org = club_id;
    this.club_name_org = club_name;
    this.join_date_org = join_date;
    this.expire_date_org = expire_date;

    this.club_id = new ArrayList<>();
    this.club_id.addAll(this.club_id_org);
    this.club_name = new ArrayList<>();
    this.club_name.addAll(this.club_name_org);
    this.join_date = new ArrayList<>();
    this.join_date.addAll(this.join_date_org);
    this.expire_date = new ArrayList<>();
    this.expire_date.addAll(this.expire_date_org);
}

请注意这里有多少重复的代码,这就是为什么它应该是一个带有自定义对象的ArrayList ...但是nvm,让(暂时)保留四个数组

不要使用这种构造:this.list = club_name;-这使listclub_name是相同的数组(对象),当您从一个项目中删除某些项目时,它将从第二个项目中删除(在事实相同)数组,我们这里需要两个分开的数组(x4)

现在所有后缀为_org的数组都包含整个列表,但是适配器应该在“工作”的数组上工作,而这些数组没有_org,就像您当前在代码中一样

现在过滤:在performFiltering中,您应该再次创建四个临时数组,并用过滤后的项目填充它们。通过_org数组过滤迭代,这些数组包含所有项

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        ArrayList club_id_temp = new Arraylist<>(),club_name_temp = new Arraylist<>(),join_date_temp = new Arraylist<>(),expire_date_temp = new Arraylist<>();
        if (constraint.length() == 0) {
            club_id_temp.addAll(club_id_org);
            club_name_temp.addAll(club_name_org);
            join_date_temp.addAll(join_date_org);
            expire_date_temp.addAll(expire_date_org);
        } else {
            for(int i=0; i<club_name_org.size(); i++){
                if(club_name_org.get(i).toLowerCase().contains(constraint.toLowerCase())){
                    club_id_temp.add(club_id_org.get(i));
                    club_name_temp.add(club_name_org.get(i));
                    join_date_temp.add(join_date_org.get(i));
                    expire_date_temp.add(expire_date_org.get(i));
                }
            }
        }

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

现在我们有一个小问题……results.values只能携带一个物体,例如一个ArrayList,而您有四个...让make class随身携带所有四个,例如声明。在适配器的底部(恰好在最后一个括号关闭整个适配器}之前)

public static class TempArrays{
    public ArrayList club_id,club_name,join_date,expire_date;
}

并将所有四个过滤后的数组打包到:

        FilterResults results = new FilterResults();
        TempArrays ta = new TempArrays();
        ta.club_id = club_id_temp;
        ta.club_name = club_name_temp;
        ta.join_date = join_date_temp;
        ta.expire_date = expire_date_temp;
        results.values = ta;
        return results;
    }

并在publishResults中解压缩此构造:

    @Override
    protected void publishResults(CharSequence constraint,FilterResults results) {
        TempArrays ta = (TempArrays) results.values;
        club_id = ta.club_id;
        club_name = ta.club_name;
        join_date = ta.join_date;
        expire_date = ta.expire_date;
        notifyDataSetChanged();
    }
再次

:四个数组是不正确的方法,看看我们这里有多少重复的行... +一些较小的解决方法,以便在过滤后传递所有四个而不是一个......实际上应该有一些{{1} }类,与此类似

ClubModel

,然后您可以处理一个包含public static class ClubModel{ public String club_id,expire_date; } 个项目的数组,而不是四个数组。查看POJO的定义