2012-11-14 10 views
5

私は最近this SO questionに賞金を追加しましたが、元の質問ではSimpleAdapterが要求され、ArrayAdapterは要求されません。したがって、この質問は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」で返されます。フィルタは文字列全体を検索しているため、スペースを検索語の一部として扱います。基本的に、私はどのように特定の文字列ではなく、複数の検索語句をスペースで区切ってリストをフィルタリングするかということです。各検索語句が項目に少なくとも1回表示されている限り、その項目は結果に返されます。

類似の質問は、以前に尋ねられたようです(SimpleAdaptersに関するこの記事の一番上にあるリンクを参照してください)。しかし、これを実現する実際の方法はありませんでした。答えは、スペースフレーズで区切られた個々の文字列に検索フレーズを分け、各文字列で複数回検索を実行し、一致する結果のみを返すことを含むと仮定します。すべて反復の...

+0

のはなぜですかその恐ろしい感覚を「何が私が行方不明ですか」?アダプターを拡張し、getFilter()メソッドをオーバーライドしたり、独自のフィルターを作成したり、performFiltering()をオーバーライドしたり、正規表現を使用して任意のコレクションを返すことはできませんか? ArrayList、あなたは必要ですか? – Simon

+0

誰かが私と同じ間違いを犯してアダプターを拡張したくないという点を忘れてしまった場合に備え、私のコメントを残しておきます。ドー!ごめんなさい! – Simon

+1

ええと、ArrayAdapterを拡張しなければ、フィルタを変更するインターフェースがないため、これを行うことはできません。なぜカスタムアダプターを書いてみませんか? – Sam

答えて

8

残念ながら、ArrayAdapterと他の組み込みアダプターでは、既存のフィルターを変更することはできません。 APIにsetFilter()メソッドはありません... 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()を最適化する可能性がある必要があります。一般的な方法は、カスタムクラスのために必要以上に遅くなります。)

+0

私のために完璧に働き、とても簡単でした!私は実際には、元のソースコードはバグでなければならないと考えています。なぜなら、prefixStringがスペースを持っていれば、単語を検索する部分は決して一致しないでしょう。 「複数の言葉は、最初からやっていればうまくいく」と言っているように、ユーザーはそれを理解することはできず、そのフィルタリングをそのまま使用するとアプリのバグだと思うことはありません。 – eselk

+0

@Sam、このソリューションには大変感謝しています。私はこのコードを適応させて、誤った順序で検索語を入力することもできます。たとえば、「もの」を検索したい場合は、「物a」と入力します。現在の状態では、これは機能しません。しかし、上記のコードには、「順序どおりの」単語を返そうとする短いセクションが含まれています。私はこの「順序」が壊れているのかもしれないのだろうか。恐らく各単語が配列に格納され、ある種の配列比較が行われているのだろうか。 – CaptainProg

+0

これは目標を達成するだろうが、コアライブラリ関数を変更することは推奨しないコードはずっと保守が容易ではありません。今後、アンドロイドの新しいバージョンにアップグレードすることは、新しいバージョンをコピーして互換性を維持しながら再度変更する必要があることを意味します。私はこれを、jQueryの独自のコピーを作成することと比較し、配列の実装が異なる方法で動作する必要があるために変更します。自己責任。 – akousmata

0

現在のところ、ArrayAdapterを上書きして独自のフィルタリング要件を実装することが唯一の解決策です。

ArrayAdapterには多くのコードが必要です。ArrayAdapterには、複製が必要な多くのプライベートオブジェクトが使用されています。

Fortunatly、他の誰か(@uʍopǝpısdn)は既にプロセスに行き、コードを書いてコミュニティで利用できるようにしました。

hereのリンクは、例の使用方法やhereのコードなどがあります。

よろしくお願いいたします。

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 +に変更して、空白で分割するようにしました。

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); 
    } 
} 
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); 
    } 

私はサムの答えからLOC + 2を削除している。このライン

for (int j = 1; j < prefixCount && loc > -1; j++) 
     loc = valueText.indexOf(' ' + prefixes[j], loc + 2); 
関連する問題