2011-11-11 3 views
27

Desidero modificare periodicamente i suggerimenti forniti da AutoCompleteTextview recuperando l'elenco da un servizio web RESTful e non riesco a farlo funzionare correttamente. Ho creato un elenco hardcoded di suggerimenti per assicurarsi che tutto funzioni:Aggiornamento dinamico di un adattatore AutoCompleteTextView

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, new String[] {"Hi", "Ho"}); 
speciesName.setAdapter(adapter);//my autocomplete tv 

Ho un TextWatcher sul TextView e quando cambia il testo che lancia una chiamata non bloccante per ottenere un nuovo elenco di suggerimenti - questa parte che ottiene una nuova lista sta funzionando bene. Poi voglio reimpostare l'adattatore, in questo modo:

public void setOptionsAndUpdate(String[] options) { 
    Log.d(TAG, "setting options"); 
    //speciesName.setAdapter((ArrayAdapter<String>)null); 
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, options); 
    speciesName.setAdapter(adapter); 
} 

Questo metodo viene chiamato, ma non funziona - l'elenco di suggerimenti o scompare o dei suggerimenti visualizzati rimangono invariati nonostante la chiamata a setAdapter.

È questo l'approccio giusto? Ho guardato su SimpleCursorAdapter ma non sono riuscito a vedere come registrare il mio servizio web come fornitore di contenuti. (E 'la forma http://www.blah.com/query?term=XX, dove il XX è l'ingresso dal mio app, e la risposta è un array JSON di stringhe.)

+0

Sto facendo qualcosa di simile QUI !!! http://stackoverflow.com/questions/12854336/autocompletetextview-backed-by-cursorloader – toobsco42

risposta

14

Questo è come mi aggiorno il mio AutoCompleteTextView:

String[] data = terms.toArray(new String[terms.size()]); // terms is a List<String> 
ArrayAdapter<?> adapter = new ArrayAdapter<Object>(activity, android.R.layout.simple_dropdown_item_1line, data); 
keywordField.setAdapter(adapter); // keywordField is a AutoCompleteTextView 
if(terms.size() < 40) keywordField.setThreshold(1); 
else keywordField.setThreshold(2); 

Ora, naturalmente, , questo è statico e non si occupa di un over-the-air suggerimenti ma, posso anche suggerire di notificare l'adattatore per le modifiche dopo aver assegnato alla AutoCompleteTextView:

adapter.notifyDataSetChanged(); 

Spero che questo aiuti.

-serkan

+0

Grazie mille, questo lo risolve per me. – jaybee

+0

@serkan: fantastico, questo è quello di cui avevo bisogno. Tuttavia, penso che la soglia non sia aggiornata, giusto? Quindi, nel tuo esempio, se 'terms.size() <40' all'inizio, la soglia rimarrà in 1 per sempre. Naturalmente è possibile eseguire nuovamente quella riga di codice per l'aggiornamento. Grazie! –

+0

Grazie Luis. Bene, quello che sto facendo lì sta solo dicendo, se ho una piccola lista di cose, posso iniziare a suggerire anche dopo che 1 personaggio è entrato; tuttavia, se la mia piscina è grande, dovrei aspettare che vengano inseriti due caratteri per suggerire qualcosa - per motivi di efficienza. Lo mantieni sicuramente 1 o 2 in ogni momento. – serkanozel

18

C'è stato un buon tutorial su questo argomento utilizzando i dati remoti l'API di Google Map per popolare un AutoCompleteTextView here.

Se è necessaria una versione memorizzata nella cache, l'ho recuperata da here. Il tutorial originale è stato cancellato, ma essenzialmente è necessario scrivere un ArrayAdapter con un filtro personalizzato in un modo simile a quello mostrato qui sotto e assegnarlo al vostro AutoCompleteTextView.

Nota: è necessario implementare un metodo di completamento automatico(), che opera tutto ciò operazione è necessario per recuperare in modo sincrono e restituisca gli articoli completamento automatico. Poiché il filtro viene richiamato in un thread in background, ciò non bloccherà il thread dell'interfaccia utente principale.

private class PlacesAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable { 
private ArrayList<String> resultList; 

public PlacesAutoCompleteAdapter(Context context, int textViewResourceId) { 
    super(context, textViewResourceId); 
} 

@Override 
public int getCount() { 
    return resultList.size(); 
} 

@Override 
public String getItem(int index) { 
    return resultList.get(index); 
} 

@Override 
public Filter getFilter() { 
    Filter filter = new Filter() { 
     @Override 
     protected FilterResults performFiltering(CharSequence constraint) { 
      FilterResults filterResults = new FilterResults(); 
      if (constraint != null) { 
       // Retrieve the autocomplete results. 
       resultList = autocomplete(constraint.toString()); 

       // Assign the data to the FilterResults 
       filterResults.values = resultList; 
       filterResults.count = resultList.size(); 
      } 
      return filterResults; 
     } 

     @Override 
     protected void publishResults(CharSequence constraint, FilterResults results) { 
      if (results != null && results.count > 0) { 
       notifyDataSetChanged(); 
      } 
      else { 
       notifyDataSetInvalidated(); 
      } 
     }}; 
    return filter; 
} 
} 
+0

Perfetto! Quel collegamento è semplicemente dannatamente dolce! Grazie per questo! :) – Jona

+0

Non blocca il controllo dell'interfaccia utente quando effettua la chiamata di rete – jonney

+0

Ho avuto la stessa domanda sul thread dell'interfaccia utente. @manyobject puoi clerificarti? –

28

io non ho avuto alcuna fortuna utilizzando adapter.notifyDataSetChanged() quando si aggiunge e modifica dei dati in modo dinamico l'adattatore. Nella mia situazione, stavo colpendo una API esterna in modo asincrono e ottenendo periodicamente un elenco di dati di completamento.

Questo codice cancella l'adattatore, e aggiunge i nuovi dati come ci si aspetterebbe. Tuttavia, ho dovuto chiamare il metodo getFilter(). Filtro per forzare la visualizzazione dei dati. Inoltre, ho dovuto filtrare esplicitamente in base al testo corrente in AutocompleteTextView perché la mia chiamata api era asincrona.

adapter.clear(); 
for (Map<String, String> map : completions) { 
    adapter.add(map.get("name")); 
} 

//Force the adapter to filter itself, necessary to show new data. 
//Filter based on the current text because api call is asynchronous. 
adapter.getFilter().filter(autocompleteTextView.getText(), null); 
+0

invece di passare in 'null' puoi passare in' autocompleteTextView' per farlo funzionare nello stesso modo di 'AutoCompleteTextView.performFiltering()' - che è il metodo solitamente chiamato dopo che il testo è stato modificato – kassim

+0

Questo. Ti possiedo una birra :) – codingpuss

+0

Grazie a @GLee. Hai reso la mia giornata. –

2

Dal momento che non sono in grado di aggiungere un commento, mi sto dando una nuova risposta Non v'è alcuna necessità di cancellare l'adattatore o chiamando adapter.getFilter(). Filtrare (...) ... Per aggiornare dinamicamente un adattatore AutoCompleteTextView, è sufficiente aggiungere nuovamente il nuovo elemento all'adattatore e impostare nuovamente ADAPTER. Per l'esempio fornito nella domanda originale, ho provato quanto segue e funziona (il codice seguente non mostra l'impostazione iniziale dell'adattatore, dal momento che più risposte coprono quella. Questo mostra solo l'aggiornamento dinamico dell'adattatore). L'aggiornamento dell'adattatore può essere affiancato al codice che aggiorna l'Elenco associato a ArrayAdapter.

adapter.add(String newSuggestion); // this goes inside a loop for adding multiple suggestions 
    speciesName.setAdapter(adapter) ; // speciesName is an AutoCompleteTextView as given in the original question.