2010-04-19 16 views
7

Próbuję uzyskać AutoCompleteTextView (ACTV), aby wyświetlać wyniki, które otrzymuję z zasobu sieciowego. Ustawiłem próg zakończenia na 2 i widzę, że żądanie jest uruchamiane, gdy wchodzę do postaci.AutoCompleteTextView nie wyświetla wyniku, nawet gdy ArrayAdapter jest zaktualizowany

Wynik, który otrzymuję, jest prawidłowy. Powiedzmy, że piszę "ca" i otrzymuję wynik "samochód" jako autouzupełnianie. Mam funkcję zwrotną, która odbiera wynik z AsyncTask i umieszcza wynik w ArrayAdapter. Następnie wywołuję .showDropDown() na ACTV i pokazane jest puste menu (połowa wielkości normalnego elementu). Następnie, jeśli wprowadzę ostatnią literę "r", a ACTV pokaże "samochód", rozwijane menu zostanie pokazane, a wynik znajdzie się nagle na liście.

To samo dzieje się, gdy wprowadziłem dwa znaki (które zwrócą prawidłowy wynik) i usunięto ostatnią literę. Po usunięciu litery "samochód" jest wyświetlany jako wartość autouzupełniania.

Czy ktoś miał ten problem? Wygląda na to, że adapter jest wypełniony wynikiem, ale wynik nie jest wyświetlany aż do następnej czynności, którą wykonuję. Próbowałem również uruchomić .notifyDataSetChanged() po dodaniu wyniku do adaptera, ale to nie powinno być potrzebne, lub?

+0

Might pomoc, jeśli dodasz swój kod. – Joe

Odpowiedz

17

Nie widząc kodu, trudno powiedzieć, co może się dziać. Ale pierwszą rzeczą, która przychodzi do głowy, jest to, że twoja prośba sieciowa dzieje się w innym wątku, a zatem Twój performFiltering() może zwracać przedwcześnie pusty zestaw wyników. W tym momencie publishResults() zwraca pusty wynik, a twoje menu rozwijane jest puste. Później AsyncTask otrzyma wynik z powrotem i doda wyniki do listy adaptera, ale z tego czy innego powodu nie zostanie jeszcze wyświetlony.

Myślę, że możesz się mylić co do potrzeby AsyncTask. Obiekt Filter już robi coś podobnego do AsyncTask: performFiltering() jest wykonywany w wątku tła, a publishResults() jest wywoływany z wątku interfejsu użytkownika, po zakończeniu funkcji performFiltering(). Możesz więc wykonać swoje żądanie sieciowe bezpośrednio w performFiltering() i ustawić wyniki w obiekcie FilterResults, a Ty nie będziesz musiał się martwić, że żądanie sieciowe jest zbyt wolne i powoduje problemy w twoim interfejsie.

Alternatywne rozwiązanie, które jest nieco bardziej skomplikowane, ale jest to, co robię w moim obiekcie Filter (ze względu na istniejącą architekturę, która wykonuje wywołania API w tle, za pomocą asynchronicznego wywołania zwrotnego zamiast kroku blokowania/synchronicznego jako wymagane dla funkcji performFiltering()), polega na użyciu zsynchronizowanego obiektu z wait()/notify() w celu wykonania monitorowania krzyżowego, więc efekt jest taki sam, jak wykonanie żądania sieciowego bezpośrednio w performFiltering(), ale tak naprawdę dzieje się to w wiele wątków:

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

Myślę jednak, że może się okazać, że najprostszym rozwiązaniem jest po prostu zrobić swoje żądanie sieciowe synchronicznie, bezpośrednio w metodzie performFiltering(). Powyższy przykład kodu jest jedną z możliwości, jeśli już posiadasz architekturę dla wywołań funkcji API asynchronicznych/wywoływanych przez wywołania zwrotne i nie chcesz zmieniać tego zachowania, aby uzyskać synchroniczne wyniki w funkcji performFiltering().

+0

świetna robota Joe! Myślę, że masz rację co do zadania asynchronicznego, zwracającego dane Po zakończeniu filtrowania peform, więc istnieje warunek wyścigu! – reidisaki

+1

dziękuję za wskazanie, że performFiltering() jest uruchamiany w wątku tła. –

+0

fajne, ale trudne –

1

Myślę, że odpowiedź Joe jest drogą do zrobienia. Jednak myślę, że powinieneś użyć CountDownLatch zamiast czekać/powiadamiać.

Powód jest taki, że z wyczekiwaniem ryzykujesz wyścig, jeśli twój interfejs API rzeczywiście powróci super szybko, zanim uruchomisz "wait()" ... w takim przypadku powiadomienie nie będzie miało żadnego efektu i zaczekaj () będzie czekać w nieskończoność. Z Klamka, kod będzie wyglądał następująco (skopiowane z Joe i modyfikowane):

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

Wreszcie, nie mam wystarczająco dużo punktów, aby zamieścić komentarz, inaczej musiałbym ...

Powiązane problemy