2011-01-05 9 views
5

Per il mio AutoCompleteTextView Ho bisogno di recuperare i dati da un webservice. Poiché può richiedere un po 'di tempo, non voglio che il thread dell'interfaccia utente non sia reattivo, quindi ho bisogno in qualche modo di recuperare i dati in un thread separato. Ad esempio, durante il recupero dei dati da SQLite DB, è molto semplice con il metodo- runQueryOnBackgroundThread. Stavo cercando altri adattatori come ArrayAdapter, BaseAdapter, ma non ho trovato nulla di simile ...Recupera AutoCompleteTextVisualizza suggerimenti dal servizio in thread separato

C'è un modo semplice come raggiungere questo obiettivo? Non posso semplicemente usare ArrayAdapter direttamente, dato che l'elenco di suggerimenti è dinamico - recupero sempre l'elenco di suggerimenti in base all'input dell'utente, quindi non può essere precaricato e memorizzato nella cache per un ulteriore utilizzo ...

Se qualcuno potrebbe dare qualche consiglio o esempi su questo argomento - sarebbe fantastico!

risposta

9

MODIFICATO: Aggiunto un modo ingenuo per evitare che il menu a discesa venga visualizzato quando si fa clic su un suggerimento.

mi fare qualcosa di simile nella mia app:

private AutoCompleteTextView mSearchbar; 
private ArrayAdapter<String> mAutoCompleteAdapter; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    mAutoCompleteAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line); 
    mSearchbar = (AutoCompleteTextView) findViewById(R.id.searchbar); 
    mSearchbar.setThreshold(3); 
    mSearchbar.setAdapter(mAutoCompleteAdapter); 
    mSearchbar.addTextChangedListener(new TextWatcher() { 

     private boolean shouldAutoComplete = true; 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) { 
      shouldAutoComplete = true; 
      for (int position = 0; position < mAutoCompleteAdapter.getCount(); position++) { 
       if (mAutoCompleteAdapter.getItem(position).equalsIgnoreCase(s.toString())) { 
        shouldAutoComplete = false; 
        break; 
       } 
      } 

     } 

     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
     } 

     @Override 
     public void afterTextChanged(Editable s) { 
      if (shouldAutoComplete) { 
       new DoAutoCompleteSearch().execute(s.toString()); 
      } 
     } 
    } 
} 

private class DoAutoCompleteSearch extends AsyncTask<String, Void, ArrayList<String>> { 
    @Override 
    protected ArrayList<String> doInBackground(String... params) { 
     ArrayList<String> autoComplete = new ArrayList<String>(); 
     //do autocomplete search and stuff. 
     return autoComplete; 
    } 

    @Override 
    protected void onPostExecute(ArrayList<String> result) { 
     mAutoCompleteAdapter.clear(); 
     for (String s : result) 
      mAutoCompleteAdapter.add(s); 
    } 
} 
+0

questa soluzione ha qualche problema nel mio caso, il menu a discesa non compare fino a quando non premo il tasto 'cancella' nella softkey per cancellare il testo digitato, è così cablato. – Longerian

+0

Questo mi ha davvero aiutato molto! Grazie! – ymerdrengene

1

avevano la stessa soluzione, tranne che il problema è che tutto va bene (variabili vengono aggiornati quando il debug), ma il completamento automatico si riempie stranamente come in

quando digito sco ha i risultati ma non compare nell'elenco ma quando ho backspace mostra il risultato per sco. Nel debug vengono aggiornate tutte le variabili che mi dicono solo che l'interfaccia utente non viene aggiornata per AutoCompleteTextView. come quando torno all'indietro viene attivato per l'aggiornamento e poi mostra l'elenco dei computer precedente (nel frattempo lo aggiorna con i nuovi elementi dell'elenco per la nuova stringa di ricerca chiunque si è imbattuto in questo problema?

10

Con l'approccio sopra, ho avuto anche questi problemi durante la digitazione molto veloce, suppongo che sia perché il filtraggio dei risultati è eseguito in modo asincrono dalla classe filtro, quindi ci possono essere problemi durante la modifica di ArrayList della scheda nel thread ui mentre il filtro è fatto.

http://developer.android.com/reference/android/widget/Filter.html

Tuttavia, con seguente approccio tutto ha funzionato bene.

public class MyActivity extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     MyAdapter myAdapter = new MyAdapter(this, android.R.layout.simple_dropdown_item_1line); 

     AutoCompleteTextView acTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1); 
     acTextView.setAdapter(myAdapter); 
    } 
} 

public class MyAdapter extends ArrayAdapter<MyObject> { 
    private Filter mFilter; 

    private List<MyObject> mSubData = new ArrayList<MyObject>(); 
    static int counter=0; 

    public MyAdapter(Context context, int textViewResourceId) { 
     super(context, textViewResourceId); 
     setNotifyOnChange(false); 

     mFilter = new Filter() { 
     private int c = ++counter; 
     private List<MyObject> mData = new ArrayList<MyObject>(); 

     @Override 
     protected FilterResults performFiltering(CharSequence constraint) { 
      // This method is called in a worker thread 
      mData.clear(); 

      FilterResults filterResults = new FilterResults(); 
      if(constraint != null) { 
      try { 
       // Here is the method (synchronous) that fetches the data 
       // from the server  
       URL url = new URL("..."); 
       URLConnection conn = url.openConnection(); 
       BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
       String line = ""; 

       while ((line = rd.readLine()) != null) { 
         mData.add(new MyObject(line)); 
       } 
      } 
      catch(Exception e) { 
      } 

      filterResults.values = mData; 
      filterResults.count = mData.size(); 
      } 
      return filterResults; 
     } 

     @SuppressWarnings("unchecked") 
     @Override 
     protected void publishResults(CharSequence contraint, FilterResults results) { 
      if(c == counter) { 
      mSubData.clear(); 
       if(results != null && results.count > 0) { 
       ArrayList<MyObject> objects = (ArrayList<MyObject>)results.values; 
       for (MyObject v : objects) 
        mSubData.add(v); 

       notifyDataSetChanged(); 
       } 
       else { 
       notifyDataSetInvalidated(); 
       } 
      } 
     } 
    }; 
    } 

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

    @Override 
    public MyObject getItem(int index) { 
    return mSubData.get(index); 
    } 

    @Override 
    public Filter getFilter() { 
    return mFilter; 
    } 
} 
+0

Sovrascrivendo tutti i metodi di base e utilizzando la propria struttura dati (mSubData) si è perso il vantaggio della sottoclasse, inclusa la sicurezza del thread che la superclasse garantisce per i dati. Basta usare i metodi add()/addAll()/clear() della superclasse. Informeranno automaticamente anche gli ascoltatori. E manterrai la sicurezza del filo. – Risadinha

Problemi correlati