6

我最近为这个 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),但我没有找到实现此目的的实际方法。我想答案将涉及将搜索短语分成单独的字符串,由空格字符分隔,然后在每个字符串上多次运行搜索,并且只返回与所有迭代匹配的结果......

4

5 回答 5

9

不幸的是,ArrayAdapter 和其他内置适配器不允许您更改它们现有的过滤器。API中没有setFilter()方法...您必须创建一个自定义适配器,该适配器执行与 ArrayAdapter 相同的功能。

为此:只需将ArrayAdapter 的源代码剪切并粘贴到项目中的一个新类中。然后在底部找到过滤器。在里面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()以适应您的需要。泛型方法比自定义类所需的速度要慢。)

于 2012-11-15T04:45:42.530 回答
4

我想我会分享我修改过的 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+ 以便它在任何空白处拆分。

于 2013-01-11T20:37:32.180 回答
1

Sam 已经给出了一个很好的答案 但是有一个很短的问题 例如,如果我想搜索“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);
于 2017-06-20T09:41:53.053 回答
0

目前,实现您想要的唯一解决方案是覆盖ArrayAdapter并实现您自己的过滤要求。

这需要大量代码,例如Filter使用ArrayAdapter大量私有对象,您需要复制这些代码。

幸运的是,其他人 (@uʍop ǝpısdn) 已经参与了这个过程,编写了代码并将其提供给社区。

您可以在此处找到指向博客的链接,包括如何使用示例以及此处的代码。

问候。

于 2012-11-14T21:52:09.763 回答
0

[补充 sam 的回复] 当您只输入空格时,您的代码会出现问题。我加了一个“如果”。

// 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);
  }
}
于 2016-04-14T23:35:48.427 回答