2012-11-14 46 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」得到恢復。由於過濾器正在查找整個字符串,因此它將空間視爲搜索術語的一部分。從本質上講,我問的是如何過濾列表不是由一個特定的字符串,而是由多個搜索項,每個由空格分隔?只要每個搜索項至少在項目中出現一次,那麼該項目應該返回結果中。

類似的問題似乎之前已經問過SO(例如,請參閱本文頂部的關於SimpleAdapters的鏈接),但是我找不到實際的方法來完成此操作。我想這個答案會涉及將搜索短語分隔成單獨的字符串,用空格字符分隔,然後在每個字符串上多次運行搜索,並且只返回匹配迭代的所有的結果...

+0

爲什麼我得到那個可怕的「我錯過什麼」的感覺?您是否可以不擴展適配器,然後重寫getFilter()方法,創建您自己的過濾器,重寫performFiltering()並使用正則表達式返回任何集合,例如ArrayList,你需要嗎? – Simon

+0

我會在這裏留下我的評論,以防有人犯我同樣的錯誤,並錯過了你不想擴展適配器的觀點。衛生署!抱歉! – Simon

+1

嗯,如果不擴展ArrayAdapter,這是無法完成的,因爲沒有接口來更改過濾器。你爲什麼不想寫一個自定義適配器? – Sam

回答

8

不幸的是,ArrayAdapter和其他內置適配器不允許您更改其現有過濾器。在API中沒有setFilter()方法...您必須創建一個自定義適配器來執行與ArrayAdapter相同的功能。

要做到這一點:只需將&粘貼ArrayAdapter's source code成爲您的項目中的新類。然後在底部找到過濾器。在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

完美的作品,我很容易,謝謝!實際上,我認爲原始源代碼必須是一個bug,因爲如果prefixString有一個空格,那麼執行單詞搜索的部分將永遠找不到匹配 - 誰會想要那個奇怪的過濾規則。這就像說「多個詞只在開始時纔有效」,如果我們按原樣使用該過濾器,用戶不會理解這一點,並認爲這是應用程序中的錯誤。 – eselk

+0

@Sam,非常感謝這個解決方案。我正在嘗試修改此代碼,以便我甚至可以按錯誤順序輸入搜索字詞。例如,如果我想搜索「某個東西」,我可能會輸入「東西a」。在目前的狀態下,這是行不通的。但是,上面的代碼包含一個簡短的部分,試圖返回單詞「按順序」。我想知道這個'順序'是否可以被破壞,也許每個單詞都存儲在一個數組中,並進行某種數組比較...... – CaptainProg

+0

這將完成目標,但是,我不建議對核心庫函數進行更改,因爲這會使代碼更不易維護。展望未來,升級到新版本的android意味着您需要複製新版本並在保持兼容性的同時再次修改它。我會比較這個做出自己的jQuery副本並修改它,只是因爲你需要數組實現以不同的方式工作。使用風險自負。 – akousmata

0

目前實現您想要的唯一解決方案是覆蓋ArrayAdapter並實施您自己的過濾要求。

這需要很多代碼,因爲FilterArrayAdapter正在使用很多私有對象,您將需要複製。

幸運的是,其他人(@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

[補充山姆的迴應] 當你只有空格時,你的代碼會有問題。我添加了一個「如果」。

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

山姆已經給出了絕佳的答案 但有一個非常短的問題 例如,如果我想搜索「一件事」,我可以輸入「事情」。這不起作用。但是,上面的代碼包含一個簡短的部分,試圖返回單詞「按順序」。您需要對其進行一些更改以獲得最佳解決方案。

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