AutoCompleteTextView non visualizzando il risultato anche quando l'ArrayAdapter viene aggiornato

StackOverflow https://stackoverflow.com/questions/2671404

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?

È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top