【问题标题】:Filtering a lot of entries using RecyclerView.Adapter使用 RecyclerView.Adapter 过滤大量条目
【发布时间】:2016-09-06 13:49:49
【问题描述】:

我有一个 adapter 扩展 RecyclerView.Adapter<RecyclerView.ViewHolder> 并实现 Filterable

我的getFilter() 实现是:

@Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                mPersonListFiltered = (List<Person>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                List<Person> filtered = new ArrayList<>();

                if (constraint == null || constraint.length() == 0) {
                    results.count = mPersonList.size();
                    results.values = mPersonList;
                } else {
                    String name, email, constr = Utils.removeDiacriticalMarks(constraint.toString());
                    for (Person person : mPersonList) {
                        name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                        email = person.getEmail().toLowerCase();
                        if (name.contains(constr) || email.contains(constr)) {
                            filtered.add(person);
                        }
                    }
                    results.count = filtered.size();
                    results.values = filtered;
                }
                return results;
            }
        };
    }

我希望在输入时过滤我的人员列表。如果我的列表大小不大于 1k,这可以正常工作,但如果我将其扩展到 5k、10k 等......它开始变得迟钝。而且我理解为什么,对于每个人,我都必须检查他们的姓名和电子邮件是否包含约束,这很糟糕。 但是我想知道在这种情况下,如果有很多条目,实现相同结果的最佳实现或替代方案是什么,换句话说,对于本地巨大列表和“即时”输入的快速过滤方法.

谢谢。

【问题讨论】:

  • 您可以在适配器中创建方法,当按钮单击此方法将返回一个模型,并且您必须在定义适配器时重写此方法这是适配器中可过滤的最佳替代解决方案

标签: java android android-filterable


【解决方案1】:

您可以预过滤您的Person 条目。

您可以为所有单字符约束创建一个Map 列表(实际上是Sets),也许还有一些多字符约束。

    Map<String, Set<Person>> mFilteredPersonMap  = new HashMap<>();

当您获得适配器的Persons 列表时,将它们添加到地图中的集合中:

                for (Person person : mPersonList) {
                    String name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                    for (char c : name.toCharArray()) {
                        if (Character.isWhitespace(c)) continue;
                        // you may want to skip other chars i.e. symbols
                        Set<Person> set = mFilteredPersonMap.get(Character.toString(c));
                        if (set == null) {
                            set = new HashSet<>();
                            mFilteredPersonMap.put(Character.toString(c), set);
                        }
                        set.add(person);
                    }
                    // do the same thing for email
                }

我使用了名称中的每个字母,因为您在原始代码中使用了contains(constr)。我的偏好是在单词边界(正则表达式"\\b(\\w)")之后搜索字符并将它们用作地图的键。

然后使用地图作为一级过滤器:

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();
            List<Person> filtered = new ArrayList<>();

            if (constraint == null || constraint.length() == 0) {
                results.count = mPersonList.size();
                results.values = mPersonList;
            } else {
                String name, email, constr = Utils.removeDiacriticalMarks(constraint.toString());
                String key = constr.substr(0, 1);
                Set set = mFilteredPersonMap.get(key);
                if (set != null) {
                    // now you are looping through a smaller collection
                    for (Person person : set) {
                        name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                        email = person.getEmail().toLowerCase();
                        if (name.contains(constr) || email.contains(constr)) {
                            filtered.add(person);
                        }
                    }
                }
                results.count = filtered.size();
                results.values = filtered;
            }
            return results;
        }

【讨论】:

  • 这种方法看起来很有趣。明天我试试,谢谢你的回答;)
  • 我没有在 IDE 或任何东西中编写代码,所以如果它有一些问题,请不要感到惊讶。大多数情况下,我只是想传达这个想法,即在过滤开始之前将您的条目分配到较小的存储桶中。让我知道结果如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
  • 2021-07-08
  • 1970-01-01
相关资源
最近更新 更多