6

最近、このSOの質問に報奨金を追加しましたが、元の質問はArrayAdapterではなくSimpleAdapterを要求していることに気付きました。したがって、この質問はArrayAdapterに関連しています。

複数の検索語を使用して、ListViewでArrayAdapterをフィルタリングできるようにしたいと思います。たとえば、私のリストに次の項目が含まれている場合:

a thing
another thing
a different thing
nothing
some thing

'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'。フィルタは文字列全体を検索するため、スペースを検索語の一部として扱います。基本的に、私が求めているのは、1つの特定の文字列ではなく、それぞれがスペースで区切られた複数の検索用語でリストをフィルタリングするにはどうすればよいですか?各検索用語がアイテムに少なくとも1回表示されている限り、そのアイテムは結果に返されます。

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

サムの優れた答えの修正版を共有したいと思いました。フィルタ文字列とフィルタリングされるテキストの両方で複数行のテキストをサポートするために、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

サムはすでに優れた答えを出しましたが、非常に短い問題が1つあります。たとえば、「もの」を検索したい場合は、「もの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

[サムの応答を補完する]スペースのみをデジタル化すると、コードに問題が発生します。「if」を追加しました。

// 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 に答える