AutoCompleteTextView non visualizzando il risultato anche quando l'ArrayAdapter viene aggiornato
-
28-09-2019 - |
Domanda
Sto cercando di ottenere un AutoCompleteTextView (ACTV) per visualizzare i risultati che sto ottenendo una da una risorsa di rete. Ho impostato il completamento-soglia di 2 e posso vedere che la richiesta è sparato quando entro a caratteri.
Il risultato che sto ottenendo è quello corretto. Consente di dire che scrivo "ca", e ottengo la "macchina" risultato come un completamento automatico. Ho una funzione di callback che riceve il risultato da un AsyncTask e mette il risultato nel ArrayAdapter. Poi chiamo .showDropDown () sulla ACTV e una discesa vuoto è mostrato (metà delle dimensioni di un elemento normale). Poi se entro l'ultima lettera "R" e la "macchina" spettacoli ACTV, la discesa è mostrato e il risultato è improvvisamente nella lista.
Lo stesso accade se ho inserito due personaggi (che restituisce un risultato valido), e il rimuovere l'ultima lettera. Quando la lettera è rimosso, "auto" viene mostrata come valore autocompletion.
Qualcuno ha avuto questo problema? Sembra che l'adattatore è riempito con il risultato, ma il risultato non mostra fino a che l'azione successiva che faccio. Ho anche provato a correre .notifyDataSetChanged () dopo aver aggiunto il risultato all'adattatore, ma che non dovrebbe essere necessario, o?
Soluzione
Senza vedere il codice, è difficile dire quello che potrebbe essere in corso. Ma la prima cosa che viene in mente è che la vostra richiesta di rete sta accadendo su un thread diverso, e quindi il vostro performFiltering()
può essere restituisce un set di risultati vuoto prematuramente. A quel punto, publishResults()
sta tornando il risultato vuoto, e la vostra discesa è vuoto. Più tardi, il vostro AsyncTask otterrà la schiena risultato, e aggiungere i risultati nella lista della scheda, ma per un motivo o un altro, non ottiene ancora visualizzato.
Penso che si può essere scambiato circa la necessità di AsyncTask però. L'oggetto filtro sta già facendo qualcosa di simile a AsyncTask: performFiltering()
è fatto in un thread in background, e publishResults()
viene chiamato dal thread dell'interfaccia utente, dopo performFiltering () è terminata. Così si può fare la tua richiesta di rete direttamente in performFiltering (), e impostare i risultati nelle FilterResults opporsi, e non dovrete preoccuparvi della richiesta di rete è troppo lenta e causare problemi nella vostra interfaccia utente.
Una soluzione alternativa, che è leggermente più complicato, ma è quello che sto facendo nel mio oggetto Filtro (grazie all'architettura che fa chiamate API in background esistente, utilizzando un callback asincrono anziché il passo di blocco / sincrono come richiesto per performFiltering ()), è quello di utilizzare un oggetto sincronizzato con wait () / notifica () per fare il monitoraggio cross-thread, quindi l'effetto è lo stesso come fare la richiesta di rete direttamente in performFiltering (), ma in realtà accadendo in multipla discussioni:
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
synchronized APIResult waitForAPI(CharSequence constraint) {
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// wait() is a Java IPC technique that will block execution until another
// thread calls the same object's notify() method.
wait();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// notify() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
notify();
}
}
Tuttavia, penso che si potrebbe scoprire che la soluzione più semplice è quello di fare solo la tua richiesta di rete in modo sincrono, direttamente nel metodo performFiltering (). L'esempio di codice di cui sopra è solo una possibilità, se si dispone già l'architettura in posto per asincrono / callback-driven chiamate API, e non si desidera modificare l'azione al fine di ottenere risultati sincroni performFiltering ().
Altri suggerimenti
Credo che la risposta di Joe è la strada da percorrere. Tuttavia, penso che si dovrebbe usare CountDownLatch al posto di attesa / notifica.
La ragione è, con wait / notify, si rischia una condizione di competizione se la vostra API effettivamente tornare super veloce prima di iniziare "wait ()" ... in questo caso, informare non avrà un effetto e wait () attenderà indefinitamente. Con Fermo, il codice sarà simile a questa (copiato da Joe e modificato):
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
CountDownLatch latch;
synchronized APIResult waitForAPI(CharSequence constraint) {
latch = new CountDownLatch(1);
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// Will wait till the count is 0...
// If the count is already 0, it'll return immediately.
latch.await();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// countDown() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
latch.countDown();
}
}
Infine, non ho abbastanza credito per pubblicare un commento, altrimenti avrei ...