我正在使用自定义ArrayAdapter在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;
}
}
我有时得到的:请注意,它很少发生。但我想完全摆脱这个错误。这是部分堆栈跟踪:
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)].
问题可能是keybord的快速输入,方法notifyDataSetChanged()没有调用。但我不确定。
答案 0 :(得分:10)
将publishResults更改为
@Override
protected void publishResults(final CharSequence contraint, final FilterResults filterResults) {
listTempPrefix = (List) results.values;
if(filterResults != null && filterResults.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
这样GUI线程就会更新结果,而不是在后台线程中运行的performFiltering。
从performFiltering中删除listTempPrefix引用,并在那里使用局部变量来存储结果并通过FilterResults返回它们
答案 1 :(得分:8)
您正在更改listTempPrefix
上的performFiltering
数组(使用clear和addAll),因此您需要调用notifyDataSetChanged
以避免此异常。
但是在ui线程上没有调用performFiltering
,因此调用notifyDataSetChanged
也会引发异常。
解决此问题的最佳方法是更改publishResults
中的listTempPrefix数组,然后调用notifyDataSetChanged
。
从listTempPrefix
方法中删除对performFiltering
所做的更改(您可能需要根据过滤器逻辑创建临时数组)。
在publishResults
上,使用listTempPrefix
中包含的值更新您的filterResults
数组,然后致电notifyDataSetChanged
。
以下是基于您的代码的示例:
@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;
}
答案 2 :(得分:0)
您必须在支持notifyDataSetChanged()
更改的数据集(ArrayList
,Array
,Cursor
等)后尽快调用Adapter
方法。否则你最终会得到Exception
。