我最近为this SO question添加了一笔赏金,但是我意识到原始问题要求使用SimpleAdapter而不是ArrayAdapter。所以,这个问题与ArrayAdapter有关:
我希望能够使用多个搜索词在ListView中过滤ArrayAdapter。例如,如果我的列表包含以下项目:
a thing
another thing
a different thing
nothing
some thing
如果我搜索'thing'然后 all ,则应在过滤结果中返回这些条款。这是可以使用以下代码完成的正常行为:
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
”中返回的所有内容。由于过滤器正在查找整个字符串,因此它将空间视为搜索项的一部分。基本上,我问的是如何过滤列表不是由一个特定的字符串,而是由多个搜索项,每个用空格分隔?只要每个搜索词在项目中至少出现一次,就应该在结果中返回该项目。
之前似乎有人问过类似的问题(例如,请参阅本文顶部的链接,它涉及SimpleAdapters),但我找不到实际的方法来实现这一点。我想答案将涉及将搜索短语分成单独的字符串,由空格字符分隔,然后在每个字符串上多次运行搜索,并仅返回匹配所有迭代的结果...
答案 0 :(得分:8)
不幸的是,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()
来满足你的需要。泛型方法比自定义类需要的慢。)
答案 1 :(得分:4)
我想我会分享我修改过的Sam的优秀答案。我只做了两个小的改动,以支持过滤字符串和被过滤的文本中的多行文本:
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 +,以便它在任何空格上分割。
答案 2 :(得分:1)
// 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 + 2for (int j = 1; j < prefixCount && loc > -1; j++)
loc = valueText.indexOf(' ' + prefixes[j], loc + 2);
答案 3 :(得分:0)
目前,实现您想要的唯一解决方案是覆盖ArrayAdapter
并实施您自己的过滤要求。
这需要大量代码,因为Filter
中的ArrayAdapter
使用了很多私有对象,您需要复制它们。
幸运的是,其他人(@uʍopǝpısdn)已经进入这个过程,编写代码并将其提供给社区。 p>
您可以找到指向博客here的链接,包括如何使用示例以及here中的代码。
问候。
答案 4 :(得分:0)
[补充山姆的回应] 当您只挖掘空格时,您的代码会出现问题。我添加了一个&#34; if&#34;。
// 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);
}
}