ArrayAdapter - 使用多个搜索词过滤
Posted
技术标签:
【中文标题】ArrayAdapter - 使用多个搜索词过滤【英文标题】:ArrayAdapter - filtering with multiple search terms 【发布时间】:2012-11-02 11:55:24 【问题描述】:我最近向this SO question 添加了一个赏金,但意识到最初的问题要求的是 SimpleAdapter 而不是 ArrayAdapter。所以,这个问题与 ArrayAdapter 有关:
我希望能够使用多个搜索词过滤 ListView 中的 ArrayAdapter。例如,如果我的列表包含以下项目:
a thing
another thing
a different thing
nothing
some thing
如果我搜索“事物”,则 所有 项应在过滤结果中返回。这是可以使用以下代码完成的正常行为:
private TextWatcher filterTextWatcher = new TextWatcher()
public void onTextChanged(CharSequence s, int start, int before, int count)
// TODO Auto-generated method stub
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
// TODO Auto-generated method stub
public void afterTextChanged(Editable s)
// TODO Auto-generated method stub
myAdapter.getFilter().filter(s.toString());
;
但是;如果我输入搜索短语“a thing
”,我希望会显示以下结果:
a thing
another thing
a different thing
事实上,我在结果“a thing
”中得到了返回。由于过滤器正在查找整个字符串,因此它将空格视为搜索词的一部分。本质上,我要问的是如何过滤列表,而不是按一个特定的字符串,而是按多个搜索词,每个搜索词用空格分隔?只要每个搜索词在项目中至少出现一次,那么该项目就应该在结果中返回。
以前似乎在 SO 上提出过类似的问题(例如,请参阅本文顶部的链接,该链接涉及 SimpleAdapters),但我没有找到实现此目的的实际方法。我想答案将涉及将搜索短语分成单独的字符串,由空格字符分隔,然后在每个字符串上多次运行搜索,并且只返回与 all 的迭代匹配的结果...
【问题讨论】:
为什么我会有那种可怕的“我错过了什么”的感觉?您能否不扩展适配器,然后覆盖 getFilter() 方法,创建自己的过滤器,覆盖 performFiltering() 并使用正则表达式返回任何集合,例如ArrayList,你需要吗? 我会在这里留下我的评论,以防有人犯了和我一样的错误,错过了你不想扩展适配器的点。嗬!对不起! 嗯,这不能在不扩展 ArrayAdapter 的情况下完成,因为没有更改过滤器的接口。为什么不想编写自定义适配器? 不不,我扩展适配器没有问题!我根本没有在我的问题中规定这一点......欢迎任何可能的解决方案:) 【参考方案1】:不幸的是,ArrayAdapter 和其他内置适配器不允许您更改它们现有的过滤器。 API 中没有 setFilter()
方法...您必须创建一个自定义适配器,该适配器执行与 ArrayAdapter 相同的功能。
要做到这一点:只需将ArrayAdapter's source code 剪切并粘贴到项目中的一个新类中。然后在底部找到过滤器。在performFiltering()
内部,我们将进行一些更改。找到标有以下注释的 if-else 块并将该代码块替换为:
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString))
newValues.add(value);
else
// Break the prefix into "words"
final String[] prefixes = prefixString.split(" ");
final int prefixCount = prefixes.length;
int loc;
// Find the first "word" in prefix
if(valueText.startsWith(prefixes[0]) || (loc = valueText.indexOf(' ' + prefixes[0])) > -1)
loc = valueText.indexOf(prefixes[0]);
// Find the following "words" in order
for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(' ' + prefixes[j], loc + 2);
// If every "word" is in this row, add it to the results
if(loc > -1)
newValues.add(value);
就是这样。我们只需要更改上面的代码部分以满足您的要求,但是我们必须将整个适配器复制到一个类中,因为没有其他方法可以进行这些更改。
(PS 由于您已经创建了一个新类,您不妨优化getView()
以满足您的需要。泛型方法比自定义类所需的速度慢。)
【讨论】:
非常适合我,而且非常简单,谢谢!我实际上认为原始源代码一定是一个错误,因为如果 prefixString 有一个空格,那么进行单词搜索的部分将永远找不到匹配项——谁会想要那个奇怪的过滤规则。这就像说“多个词只有在开始时才有效”,没有用户会理解这一点,并且如果我们按原样使用过滤器,他们会认为这是我们应用程序中的一个错误。 @Sam,非常感谢这个解决方案。我正在尝试调整此代码,以便我什至可以以错误的顺序输入搜索词。例如,如果我想搜索“a thing”,我可能会输入“thing a”。在当前状态下,这是行不通的。但是,上面的代码包含一个简短的部分,它试图返回“按顺序”这个词。我想知道这个“顺序”是否会被打破,也许每个单词都存储在一个数组中并进行某种数组比较...... 这将实现目标,但是,我不建议对核心库函数进行更改,因为这会使代码的可维护性大大降低。展望未来,升级到新版本的 android 意味着您需要复制新版本并再次修改它,同时保持兼容性。我会将其与制作自己的 jQuery 副本并修改它进行比较,只是因为您需要数组实现以不同的方式工作。使用风险自负。【参考方案2】:我想我会分享我修改后的 Sam 出色答案的版本。我刚刚做了 2 个小改动来支持过滤字符串和被过滤的文本中的多行文本:
final String valueText = value.toString().toLowerCase().replace('\r', ' ').replace('\n', ' ');
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString))
newValues.add(value);
else
// Break the prefix into "words"
final String[] prefixes = prefixString.split("\\s+");
final int prefixCount = prefixes.length;
int loc;
// Find the first "word" in prefix
if(valueText.startsWith(prefixes[0]) || (loc = valueText.indexOf(' ' + prefixes[0])) > -1)
loc = valueText.indexOf(prefixes[0]);
// Find the following "words" in order
for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(' ' + prefixes[j], loc + 2);
// If every "word" is in this row, add it to the results
if(loc > -1)
newValues.add(value);
我只是将 \r 和 \n 替换为空格。如果需要,您还可以删除其他空格,例如 \t,或更改为正则表达式......对我来说 \r 和 \n 就足够了。我还将 split() 更改为 \s+ 以便它在任何空白处拆分。
【讨论】:
【参考方案3】:山姆已经给出了很好的答案 但是有一个很短的问题 例如,如果我想搜索“a thing”,我可能会输入“thing a”。这行不通。但是,上面的代码包含一个简短的部分,它试图返回“按顺序”这个词。您需要对其进行一些更改以获得最佳解决方案。
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString))
newValues.add(value);
else
// Break the prefix into "words"
final String[] prefixes = prefixString.split(" ");
final int prefixCount = prefixes.length;
int loc;
// Find the first "word" in prefix
if(valueText.startsWith(prefixes[0]) || (loc = valueText.indexOf(' ' + prefixes[0])) > -1)
loc = valueText.indexOf(prefixes[0]);
// Find the following "words"
for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(prefixes[j]);
// If every "word" is in this row, add it to the results
if(loc > -1)
newValues.add(value);
我已经从 Sam 的回答中删除了 loc + 2
for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(' ' + prefixes[j], loc + 2);
【讨论】:
【参考方案4】:目前,实现您想要的唯一解决方案是覆盖 ArrayAdapter
并实现您自己的过滤要求。
这需要大量代码,因为ArrayAdapter
中的Filter
使用大量私有对象,您需要复制这些对象。
幸运的是,其他人 (@uʍopǝpısdn) 已经参与了该流程,编写了代码并将其提供给社区。p>
您可以找到指向博客here 的链接,包括如何使用示例,以及here 中的代码。
问候。
【讨论】:
【参考方案5】:[补充山姆的回应] 当您仅对空格进行数字化时,您的代码将出现问题。我加了一个“如果”。
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString))
newValues.add(value);
else
// Break the prefix into "words"
final String[] prefixes = prefixString.split(" ");
final int prefixCount = prefixes.length;
// HERE CHANGE
if (prefixCount>0)
int loc;
// Find the first "word" in prefix
if (valueText.startsWith(prefixes[0]) || (loc = valueText.indexOf(' ' + prefixes[0])) > -1)
loc = valueText.indexOf(prefixes[0]);
// Find the following "words" in order
for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(' ' + prefixes[j], loc + 2);
// If every "word" is in this row, add it to the results
if (loc > -1)
newValues.add(value);
【讨论】:
以上是关于ArrayAdapter - 使用多个搜索词过滤的主要内容,如果未能解决你的问题,请参考以下文章
在 Drupal 7 中,如何使用“搜索视图”模块将搜索词过滤器添加到我的视图中?