Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalStateException: The content of the adapter has changed but ListView did not receive a notification

I'm using a custom ArrayAdapter to set the adapter on an AutocompleteTextView (AddressAdapter extends ArrayAdapter).

public class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable {
private ArrayList<String> mData;
ArrayList<String> listTempPrefix = new ArrayList<String>();
ArrayList<String> listTemp = new ArrayList<String>();
String valueText;
String[] words;
String ulcase;

public AutoCompleteAdapter(Context context, int textViewResourceId, ArrayList<String> bS) {
    super(context, textViewResourceId);
    mData = bS;//new ArrayList<String>();
}

@Override
public int getCount()
{
    synchronized (listTempPrefix)
    {
        return listTempPrefix.size();
    }
}

@Override
public String getItem(int index)  
{
    synchronized (listTempPrefix)
    {
        try {
            //Log.e("Error", listTempPrefix.get(index));
            return listTempPrefix.get(index);
        } catch(IndexOutOfBoundsException e) {
            Log.e("Error", "IndexOutOfBoundsException");
            return "";
        }
    }

}

@Override
public Filter getFilter()
{
    Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint)
        {

            FilterResults filterResults = new FilterResults();
            synchronized (filterResults)
            {
                listTempPrefix.clear();
                listTemp.clear();
                //Log.e("1", "1");

                try {
                if(constraint != null) {
                    // A class that queries a web API, parses the data and returns an ArrayList<Style>
                    //StyleFetcher fetcher = new StyleFetcher();
                    //try {
                        //mData = fetcher.retrieveResults(constraint.toString());
                    //}
                    //catch(Exception e) {}
                    // Now assign the values and count to the FilterResults object


                    for(String value: mData) {
                        valueText = value.toLowerCase();

                        //System.out.println("constraintH - " + constraint);

                        constraint.toString().toLowerCase();
                        ulcase = constraint.toString().toLowerCase();
                        //System.out.println("ulcase - " + ulcase);

                        if (valueText.startsWith(ulcase)) {
                            listTempPrefix.add(value);
                        } else {
                            words = valueText.split(" ");
                            //final int wordCount = words.length;

                            // Start at index 0, in case valueText starts with space(s)
                            for (int k = 0; k < words.length; k++) {
                                if (words[k].startsWith(ulcase)) {
                                    listTemp.add(value);
                                    break;
                                }
                            }
                        }

                        ///listTemp.add(mData.get(i));
                        //filterResults.count = mData.size();
           //           System.out.println("mData" + i + mData.get(i));
                    }
                    //Log.e("2", "2");
           //       System.out.println("size " + listTemp.size() + " value" + listTemp);

                    listTempPrefix.addAll(listTemp);

                    filterResults.values = listTempPrefix;

                    filterResults.count = listTempPrefix.size();
                    //System.out.println("size " + filterResults.count + " value" + filterResults.values);

                    //System.out.println("constraint" + constraint);

                }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    }
                return filterResults;
            }
        }

        @Override
        protected void publishResults(CharSequence contraint, FilterResults filterResults) 
        {
            synchronized (filterResults)
            {
                if(filterResults != null && filterResults.count > 0) {
                notifyDataSetChanged();
                //Log.e("notifyDataSetChanged", "notifyDataSetChanged");
                }
                else {
                    notifyDataSetInvalidated();
                    //Log.e("notifyDataSetInvalidated", "notifyDataSetInvalidated");
                }
            }
        }
    };
    return myFilter;
}

}

What I got sometimes: please notice, it happens really rarely. But I'd like to get rid of this bug completely. Here is partial stacktrace:

java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(-1, class android.widget.AutoCompleteTextView$DropDownListView) with Adapter(class com.example.test.AutoCompleteAdapter)].

The problem maybe that the fast input from keybord, method notifyDataSetChanged () issn't call. But i'am not sure.

like image 827
user1528799 Avatar asked Oct 31 '12 10:10

user1528799


2 Answers

Change publishResults to

@Override 
protected void publishResults(final CharSequence contraint, final FilterResults filterResults) { 
    listTempPrefix = (List) results.values;
    if(filterResults != null && filterResults.count > 0) {
        notifyDataSetChanged();
    } else {
        notifyDataSetInvalidated();
    }
}

This way the GUI thread updates the results instead of performFiltering which runs in a background thread.

Remove listTempPrefix references from performFiltering and use a local variable there to store the results and return them through FilterResults

like image 148
nhpatt Avatar answered Jan 08 '23 04:01

nhpatt


You're changing the listTempPrefix array on performFiltering (using clear and addAll), so you would need to call notifyDataSetChanged to avoid this exception.

But performFiltering is not called on the ui thread, so calling notifyDataSetChanged would also raise an exception.

The best way to solve this problem is changing the listTempPrefix array inside publishResults, and then call notifyDataSetChanged.

Remove the changes made to listTempPrefix from the performFiltering method (you might need to create a temp array depending on your filter logic).

On publishResults, update your listTempPrefix array with the values contained on filterResults, and call notifyDataSetChanged.

Here is an exemple based on your code:

@Override
public Filter getFilter()
{
    Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint)
        {

            FilterResults filterResults = new FilterResults();
            synchronized (filterResults)
            {
                //listTempPrefix.clear(); // Don't change listTempPrefix here
                 ...
                        ulcase = constraint.toString().toLowerCase();
                        //System.out.println("ulcase - " + ulcase);

                        if (valueText.startsWith(ulcase)) {
                            //listTempPrefix.add(value); // Don't change listTempPrefix
                            // To keep your logic you might need an aux array 
                            // for this part
                        } else {
                            ...
                        }

                    //listTempPrefix.addAll(listTemp); // Don't change it

                    filterResults.values = listTempPrefix; // No problem here

                    filterResults.count = listTempPrefix.size(); // No problem here
                    //System.out.println("size " + filterResults.count + " value" + filterResults.values);

                    //System.out.println("constraint" + constraint);

                }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    }
                return filterResults;
            }
        }

        @Override
        protected void publishResults(CharSequence contraint, FilterResults filterResults) 
        {
            // At this point, make the changes you need to listTempPrefix
            // using filterResults.values
            synchronized (filterResults)
            {
                if(filterResults != null && filterResults.count > 0) {
                notifyDataSetChanged();
                //Log.e("notifyDataSetChanged", "notifyDataSetChanged");
                }
                else {
                    notifyDataSetInvalidated();
                    //Log.e("notifyDataSetInvalidated", "notifyDataSetInvalidated");
                }
            }
        }
    };
    return myFilter;
}
like image 45
Marcelo Avatar answered Jan 08 '23 05:01

Marcelo