AutoCompleteTextView does not display result even when updating ArrayAdapter

I am trying to get an AutoCompleteTextView (ACTV) to display the results that I get from a network resource. I set the completion-treshold to 2, and I see that the request is triggered when characters are entered.

The result I get is correct. Suppose I write β€œca” and I get the result β€œcar” as autocomplete. I have a callback function that gets the result from AsyncTask and puts the result in an ArrayAdapter. Then I call .showDropDown () on ACTV and a blank drop-down menu is displayed (half the size of a regular item). Then, if I enter the last letter "r" and ACTV shows "car", a drop-down menu is displayed and the result unexpectedly appears in the list.

The same thing happens if I enter two characters (which returns a valid result) and delete the last letter. When a letter is deleted, β€œcar” is displayed as an autocomplete value.

Has anyone had this problem? It looks like the adapter is populated with the result, but the result is not displayed until the next action. I also tried running .notifyDataSetChanged () after adding the result to the adapter, but this is not needed, or?

+7
android adapter autocompletetextview
source share
2 answers

Without seeing your code, it's hard to say what might happen. But the first thing that comes to mind is that your network request is in a different thread, and therefore your performFiltering() may return an empty result set prematurely. At this point, publishResults() returns an empty result, and your drop-down list is empty. Later, your AsyncTask will return its result and add the results to the list of adapters, but for one reason or another it is not yet displayed.

I think you may be mistaken in the need to use AsyncTask. The Filter object already does something similar to AsyncTask: performFiltering() is executed in the background thread, and publishResults() is called from the user interface thread after the completion of the Filtering () function. Thus, you can execute your network request directly in the performFiltering () function and set the results in the FilterResults object, and you don’t have to worry about the network request being too slow and causing problems in the user interface.

An alternative solution, which is a little more complicated, but what I am doing in the Filter object (due to the existing architecture that calls the API in the background using an asynchronous callback instead of the blocking / synchronous step as needed for performFiltering ()) is using a synchronized object with the wait () / notify () function to perform cross-flow control, therefore the effect is the same as when executing a network request directly in performFiltering (), but it actually occurs in several topics:

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

However, I think you may find that the simplest solution is to simply execute the network request synchronously, directly in the performFiltering () method. The above code example is just one option if you already have an architecture for asynchronous / API callbacks and you don't want to change this behavior to get synchronous results in performFiltering ().

+17
source share

I think Joe's answer is the way to go. However, I think you should use CountDownLatch instead of wait / notify.

The reason is that with wait / notify you risk a race condition if your API really comes back super fast before you start "wait ()" ... in which case the notification will have no effect and wait () will wait indefinitely. With a latch, the code will look like this (copied from Joe and modified):

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

Finally, I do not have enough loans to post a comment, otherwise I would ...

+1
source share

All Articles