I created a listview with adapter. Implemented search with filter. It is working fine. But sometimes application is crashing with the following error:
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. Make sure your adapter calls notifyDataSetChanged() when its content changes.
In ListView activity I am using the below code to invoke search when user types something in the textbox.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_view_layout);
mAdapter = new ListViewAdapter(this);
...
mSearchEditText = (EditText) findViewById(R.id.search_text);
mSearchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.v("ListViewActivity", "SearchTextBox: onTextChanged <" + s + ">");
mAdapter.getFilter().filter(s);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
In ListViewAdapter I used filter to implement search in the listview:
public class ListViewAdapter extends BaseAdapter implements Filterable{
private ArrayList<ListItem> mOriginalList;
private ArrayList<ListItem> mDisplayedList;
...
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
Log.v("ListViewAdapter", "Filter: publishResults constraint<" + constraint + ">");
notifyDataSetChanged();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
Log.v("ListViewAdapter", "Filter: performFiltering constraint<" + constraint + ">");
FilterResults filterResults = new FilterResults();
mDisplayedList.clear();
if (constraint == null || constraint.length() == 0) {
mDisplayedList.addAll(mOriginalList);
filterResults.count = mDisplayedList.size();
filterResults.values = mDisplayedList;
} else {
String searchText = constraint.toString().toLowerCase();
for (ListItem listItem : mOriginalList) {
if (listItem.getKeyName().toLowerCase().contains(searchText)
|| listItem.getKeyValue().toLowerCase().contains(searchText)) {
mDisplayedList.add(listItem);
}
}
filterResults.count = mDisplayedList.size();
filterResults.values = mDisplayedList;
}
Log.v("ListViewAdapter", "Filter: performFiltering filterResults.count<" + filterResults.count + ">");
return filterResults;
}
};
return filter;
}
Issue is resolved. I moved the code to update mDisplayedList to publishResults() instead of keep on updating in performFiltering() for loop. Update mDisplayedList and call notifyDataSetChanged() together. I think this is the best solution.
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
Log.v("ListViewAdapter", "Filter: publishResults constraint<" + constraint + ">");
if (results.count == 0) {
mDisplayedList.clear();
notifyDataSetInvalidated();
} else {
mDisplayedList.clear();
mDisplayedList.addAll((ArrayList<ListItem>) results.values);
notifyDataSetChanged();
}
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
Log.v("ListViewAdapter", "Filter: performFiltering constraint<" + constraint + ">");
FilterResults filterResults = new FilterResults();
ArrayList<ListItem> filteredList = new ArrayList<ListItem>();
if (constraint == null || constraint.length() == 0) {
filteredList.addAll(mOriginalList);
} else {
String searchText = constraint.toString().toLowerCase();
for (ListItem listItem : mOriginalList) {
if (listItem.getKeyName().toLowerCase().contains(searchText)
|| listItem.getKeyValue().toLowerCase().contains(searchText)) {
filteredList.add(listItem);
}
}
}
filterResults.count = filteredList.size();
filterResults.values = filteredList;
Log.v("ListViewAdapter", "Filter: performFiltering filterResults.count<" + filterResults.count + ">");
return filterResults;
}
};
return filter;
}