ArrayAdapter - 使用多个搜索词进行过滤

Cap*_*rog 6 android listview filtering android-arrayadapter

我最近在这个SO问题上添加了一笔赏金,但是我意识到原始问题要求使用SimpleAdapter而不是ArrayAdapter.所以,这个问题与ArrayAdapter有关:

我希望能够使用多个搜索词在ListView中过滤ArrayAdapter.例如,如果我的列表包含以下项目:

a thing
another thing
a different thing
nothing
some thing
Run Code Online (Sandbox Code Playgroud)

如果我搜索"事物" ,则应在过滤结果中返回所有条款.这是可以使用以下代码完成的正常行为:

    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());
    }
};
Run Code Online (Sandbox Code Playgroud)

然而; 如果我输入搜索短语' a thing',我希望显示以下结果:

a thing
another thing
a different thing
Run Code Online (Sandbox Code Playgroud)

事实上,我得到的所有结果都是' a thing'.由于过滤器正在查找整个字符串,因此它将空间视为搜索项的一部分.基本上,我问的是如何过滤列表不是由一个特定的字符串,而是由多个搜索项,每个用空格分隔?只要每个搜索项在项中至少出现一次,那么应该在结果中返回该项.

在SO之前似乎已经提出了类似的问题(例如,请参阅本文顶部的链接,它涉及SimpleAdapters),但我找不到实际的方法来实现这一点.我想答案是将搜索短语分成单独的字符串,由空格字符分隔,然后在每个字符串上多次运行搜索,只返回匹配所有迭代的结果......

Sam*_*Sam 8

不幸的是,ArrayAdapter和其他内置适配器不允许您更改现有的过滤器.setFilter()API中没有方法...您必须创建一个执行与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);
}
Run Code Online (Sandbox Code Playgroud)

而已.我们只需要更改上面的代码部分以满足您的要求,但我们必须将整个适配器复制到一个类中,因为没有其他方法可以进行这些更改.

(PS既然你已经创建了一个新类,你也getView()可以根据自己的需要进行优化.通用方法比自定义类需要的慢.)


ese*_*elk 5

我想我会分享我对 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);
}
Run Code Online (Sandbox Code Playgroud)

我只是将 \r 和 \n 替换为空格。如果需要,您还可以删除其他空格,例如 \t,或更改为正则表达式......对我来说 \r 和 \n 就足够了。我还将 split() 更改为 \s+,以便它在任何空白处拆分。