【问题标题】:How to implement Search Filter with RecyclerView如何使用 RecyclerView 实现搜索过滤器
【发布时间】:2020-09-07 13:18:24
【问题描述】:

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

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

所以我试图弄清楚如何实现它,但我在过滤器和所有这些事情上遇到了困难。我看到的每个教程都使用 List (E),而我的 CustomeAdpter 获取 ArrayList,我无法理解如何使其与 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, club_name, join_date,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);
    }
 }}

【问题讨论】:

  • @Nik 看,我现在编辑我的问题,我尝试了你放在这里的帖子中的一些方法,但是运行它之后,当我尝试搜索某些东西时没有任何反应。

标签: android android-recyclerview android-sqlite android-search


【解决方案1】:

customAdapter.getFilter();OnQueryTextListener 的两种方法中都有customAdapter.getFilter(); 行,它会为您返回过滤器。这就是您所要求的全部内容,您从未将String 传递给此过滤器。这一行应该是这样的

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

由于 cmets 而编辑:

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

您只过滤和发布其中一个数组 (club_name = (ArrayList&lt;String&gt;) results.values;),但您仍然通知 adapter 该列表在 getItemCount 中的项目数等于 club_id.size();。这就是为什么你得到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,你有四个......让我们带着所有四个为你的目的上课,声明例如在适配器底部(就在关闭整个适配器的最后一个括号之前}

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();
    }

再次:四个数组不是正确的方法,看看我们这里有多少重复的行...+一些小的解决方法可以在过滤后传递所有四个而不是一个...实际上应该有一些 ClubModel 类,和这个类似

public static class ClubModel{
    public String club_id, club_name, join_date, expire_date;
}

然后您可以使用ClubModel 项目处理一个数组,而不是四个数组。查看POJO定义

【讨论】:

  • 添加“过滤器(查询);”仍然没有发生任何事情0
  • performFiltering 方法被调用了吗?
  • 是的,但停在中间,(我用 Log.... 消息检查它)当我开始在搜索中输入内容时出现此错误:W/Filter:在 performFiltering 期间发生异常()! java.lang.ClassCastException: java.lang.String cannot be cast to com.camel.myclub.CustomAdapter 它把我带到这一行:for "(CustomAdapter item : originalList).."
  • List&lt;CustomAdapter&gt; - 为什么要创建适配器列表?适配器是唯一的(最常见的),里面的数据可能是一个包含多个项目的数组。所以在getFilteredResults 中,你应该处理你的数据数组(ArrayListclub_idclub_name 等),而不是适配器数组。删除protected List&lt;CustomAdapter&gt; list;protected List&lt;CustomAdapter&gt; originalList;,过滤不需要它们,相反您可能需要一些临时数组来过滤数据(当有人清除请求整个列表的查询时保持原始数组不变)
  • 好的,所以我改变了你说的每件事,加上我将“list”和“originalList”更改为 ArrayList 在做了一些测试后我发现过滤器工作(我看到了日志中的每个更改我打字的时间),但它并没有改变 RecyclerView 上的任何东西......你有 discord 我们可以谈谈吗?
猜你喜欢
  • 2017-04-06
  • 2019-04-06
  • 1970-01-01
  • 1970-01-01
  • 2019-09-19
  • 2017-11-11
  • 1970-01-01
  • 2021-11-25
  • 2018-07-04
相关资源
最近更新 更多