2012-11-14 15 views
5

Ich habe kürzlich eine Prämie zu this SO question hinzugefügt, aber realisieren Sie die ursprüngliche Frage fragt nach einem SimpleAdapter und nicht nach einem ArrayAdapter. Diese Frage bezieht sich also auf den ArrayAdapter:ArrayAdapter - Filterung mit mehreren Suchbegriffen

Ich möchte einen ArrayAdapter in einem ListView mit mehreren Suchbegriffen filtern können. Wenn zum Beispiel meine Liste enthält die folgenden Elemente:

a thing 
another thing 
a different thing 
nothing 
some thing 

Wenn ich für ‚Ding‘ zu suchen dann alle die Begriffe sollten in den Filterergebnissen zurückgegeben werden. Dies ist ein normales Verhalten, das unter Verwendung des folgenden Codes erreicht werden kann:

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

Jedoch; wenn ich den Suchbegriff ‚a thing‘ eingeben, erwarte ich, dass die folgenden Ergebnisse angezeigt werden:

a thing 
another thing 
a different thing 

In der Tat, alles, was ich bekomme im Ergebnis zurückgegeben ‚a thing‘. Da der Filter nach der gesamten Zeichenfolge sucht, behandelt er den Bereich als Teil des Suchbegriffs. Was ich im Grunde frage ist, wie filtere ich die Liste nicht durch eine bestimmte Zeichenfolge, sondern durch mehrere Suchbegriffe, die jeweils durch Leerzeichen getrennt sind? Solange jeder der Suchbegriffe mindestens einmal im Artikel erscheint, sollte dieser Artikel im Ergebnis zurückgegeben werden.

Ähnliche Fragen scheinen zuvor schon auf SO gestellt worden zu sein (siehe zum Beispiel den Link oben in diesem Post, der SimpleAdapters betrifft), aber nirgends habe ich einen tatsächlichen Weg gefunden, dies zu erreichen. Ich nehme an, die Antwort würde beinhalten, den Suchausdruck in einzelne Zeichenfolgen zu trennen, die durch das Leerzeichen begrenzt sind, und dann die Suche mehrmals in jeder Zeichenfolge ausführen und nur die Ergebnisse zurückgeben, die für der Iterationen ...

übereinstimmen
+0

Warum erhalte ich das schrecklich "was bin ich dabei" Gefühl? Können Sie den Adapter nicht erweitern, überschreiben Sie dann die Methode getFilter(), erstellen Sie einen eigenen Filter, überschreiben Sie performFiltering() und verwenden Sie eine Regex, um eine beliebige Sammlung zurückzugeben, z. ArrayList, brauchst du? – Simon

+0

Ich werde meinen Kommentar hier lassen, falls jemand den gleichen Fehler macht wie ich und den Punkt vermisst, dass Sie den Adapter nicht verlängern wollen. Do! Es tut uns leid! – Simon

+1

Hmm, das kann nicht ohne ArrayAdapter erweitert werden, da es keine Schnittstelle gibt, um den Filter zu ändern. Warum willst du keinen benutzerdefinierten Adapter schreiben? – Sam

Antwort

8

Leider können Sie mit ArrayAdapter und anderen integrierten Adaptern die vorhandenen Filter nicht ändern. Es gibt keine setFilter() Methode in der API ... Sie müssen einen benutzerdefinierten Adapter erstellen, der die gleichen Funktionen wie ArrayAdapter ausführt.

Um dies zu tun: einfach & einfügen ArrayAdapter's source code in eine neue Klasse in Ihrem Projekt. Dann finden Sie den Filter am unteren Rand. Innerhalb performFiltering() werden wir ein paar Änderungen vornehmen. Finden Sie den if-else-Block mit dem folgenden Kommentar markiert und ersetzt diesen Codeblock mit diesem:

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

Das ist es.Wir mussten nur den obigen Codeabschnitt ändern, um Ihre Anforderungen zu erfüllen, aber wir müssen den gesamten Adapter in eine Klasse kopieren, da es keine andere Möglichkeit gibt, diese Änderungen vorzunehmen.

(PS Da Sie bereits eine neue Klasse erstellt haben, können Sie auch getView() optimieren Sie Bedürfnisse. Die generische Methode ist langsamer, als es für eine benutzerdefinierte Klasse sein muss.)

+0

Funktioniert perfekt für mich und war sehr einfach, danke! Ich glaube eigentlich, dass der ursprüngliche Quellcode ein Bug sein muss, da der Teil, der die Wortsuche durchführt, niemals eine Übereinstimmung finden wird, wenn prefixString ein Leerzeichen hat - wer würde diese seltsame Filterregel jemals wollen? Es ist so, als würde man sagen, dass "mehrere Wörter nur funktionieren, wenn sie am Anfang stehen", kein Benutzer würde das jemals verstehen und würde meinen, dass es ein Fehler in unseren Apps ist, wenn wir diese Filterung so wie sie sind verwenden. – eselk

+0

@Sam, vielen Dank für diese Lösung. Ich versuche, diesen Code so anzupassen, dass ich sogar Suchbegriffe in der falschen Reihenfolge eingeben kann. Zum Beispiel, wenn ich nach "einer Sache" suchen wollte, könnte ich "Sache a" eingeben. Im aktuellen Zustand wird dies nicht funktionieren. Der obige Code enthält jedoch einen kurzen Abschnitt, der versucht, die Wörter "in Reihenfolge" zurückzugeben. Ich frage mich, ob diese "Ordnung" gebrochen werden könnte, vielleicht mit jedem Wort in einem Array gespeichert und eine Art Array-Vergleich ... – CaptainProg

+0

Dies wird das Ziel erreichen, aber ich empfehle nicht Änderungen an Kern-Bibliothek Funktionen wie das macht der Code viel weniger wartbar. Wenn Sie nach dem Upgrade auf neue Versionen von Android fortfahren, müssen Sie die neue Version kopieren und erneut modifizieren, während die Kompatibilität erhalten bleibt. Ich würde dies mit dem Erstellen einer eigenen Kopie von jQuery vergleichen und sie ändern, nur weil Sie die Array-Implementierung benötigen, um anders zu arbeiten. Benutzung auf eigene Gefahr. – akousmata

0

Derzeit ist die einzige Lösung, um zu erreichen, was Sie wollen, überschreiben die ArrayAdapter und implementieren Sie Ihre eigenen Filteranforderungen.

Dies erfordert eine Menge Code, da Filter in ArrayAdapter viele private Objekte verwendet, die Sie replizieren müssen.

Fortunatly, jemand anderes (@ uʍop ǝpısdn) ging bereits zum Prozess, schrieb den Code und stellte ihn für die Gemeinschaft zur Verfügung.

Sie finden den Link zu Blog here einschließlich der Verwendung von Beispielen und den Code in here.

Grüße.

4

Ich dachte, ich würde meine modifizierte Version von Sams exzellenter Antwort teilen. Ich habe nur 2 kleine Änderungen mehrzeiligen Text zur Unterstützung sowohl in der Filterkette und der Text gefiltert werden:

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

I nur \ r \ n mit Leerzeichen ersetzen. Sie könnten auch anderen Leerraum entfernen, wie zB \ t, oder zu einem regulären Ausdruck wechseln ... für mich und \ n war genug. Ich habe auch das split() in \ s + geändert, so dass es sich auf jedem Leerraum aufteilt.

0

[Ergänzt die Antwort des Sam] Ihr Code wird Probleme haben, wenn Sie nur Leerzeichen löschen. Ich habe ein "wenn" hinzugefügt.

// 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

Sam hat bereits eine ausgezeichnete Antwort Aber es gibt ein sehr kurzes Problem Zum Beispiel gegeben, wenn ich für ein „Ding“ suchen wollte, könnte ich „was eine“ eingeben. Dies wird nicht funktionieren. Der obige Code enthält jedoch einen kurzen Abschnitt, der versucht, die Wörter "in Reihenfolge" zurückzugeben. Und Sie müssen einige Änderungen vornehmen, um die beste Lösung zu erhalten.

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

I loc + 2 von Sam Antwort entfernt haben diese Linie

for (int j = 1; j < prefixCount && loc > -1; j++) 
     loc = valueText.indexOf(' ' + prefixes[j], loc + 2); 
Verwandte Themen