问题
I have an AutocompleteTextView working with an ArrayAdapter. The adapter is updated from a webservice, when the text changes. This updated is done in an AsyncTask, what is supposed to be a good practice. This is working more or less, because the suggestions after every key pressed are based on the strings retrieved in the previous key pressed. There is a couple of problems related is this page, but none of the answers works for me. Anyway, I had a solution, but is inefficient and I don't know why the "official solution" fails.
I think that the key is in the function that udpates de ArrayAdapter in the background. This it what I do in the asynchronous call to the webservices:
private class DoAutoCompleteSearch extends AsyncTask<String, Void, Map<String, String>> {
@Override
protected Map<String, String> doInBackground(String... params) {
// Ask the webservice for data
Map<String, String> autoComplete = GetResource.dataList(params[0]);
return autoComplete;
}
@Override
protected void onPostExecute(Map<String, String> result) {
//mAutoCompleteAdapter.clear(); * This should work but does not *
/* If it is set a new adapter in the AutoCompleteTextView, the whole thing works properly */
mAutoCompleteAdapter = new ArrayAdapter<String>(mAutoCompleteAdapter.getContext(), android.R.layout.simple_dropdown_item_1line);
mACTV.setAdapter(mAutoCompleteAdapter);
for (Map.Entry<String, String> entry : result.entrySet()) {
mAutoCompleteAdapter.add(entry.getKey());
}
}
}
I have tried with mAutoCompleteAdapter.clear() and setting mAutoCompleteAdapter.notifyDataSetChanged() everywhere, but it is useless.
回答1:
I also tried alot to get this approach working, but didn't succeed any better than yourself. But I found another way to accomplish what you want; extend ArrayAdapter and implement Filterable. This class will be doing the actual fetching from the database, when called by the AutoCompleteTextView in a worker thread:
public class MyAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private List<String> mData = new ArrayList<String>();
private Server mServer;
public MyAutoCompleteAdapter(Server server, Context context, int textViewResourceId) {
super(context, textViewResourceId);
mServer = server;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public String getItem(int index) {
return mData.get(index);
}
@Override
public Filter getFilter() {
Filter myFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
// This method is called in a worker thread
FilterResults filterResults = new FilterResults();
if(constraint != null) {
try {
// Here is the method (synchronous) that fetches the data
// from the server
List<String> results = mServer.searchForItems(constraint.toString());
filterResults.values = results;
filterResults.count = results.size();
}
catch(Exception e) {}
}
return filterResults;
}
@Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if(results != null && results.count > 0) {
mData = (List<String>)results.values;
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}
};
return myFilter;
}
}
EDIT: I have improved the above code since I sometimes got the exception java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification
. To fix this, I moved the updating of mData
to publishResults()
.
回答2:
This code snippet worked for me in my app. Put this in your activity that contains the ListView
:
@Override
public void onActivityResumed() //
{
if (favoritesHaveChanged(activity, productString)) //
{
m_adapter.clear();
performAsyncTaskWithCallBack(activity);
}
}
The callback will be an event listener that is triggered. Typically with AsyncTasks
you can use onPostExecute()
in your AsyncTask
.
来源:https://stackoverflow.com/questions/6546376/arrayadapter-is-updated-late-from-webservice-in-autocompletetextadapter