我有一个由自定义适配器(扩展ListActivity
)支持的自定义BaseAdapter
。 Inspired by this,布局还包含EditText
和TextWatcher
。适配器实现Filterable
。 TextWatcher.onTextChanged(...)
只是调用adapter.getFilter().filter(string);
,就像哈米在另一个问题中所建议的那样。
问题:当设备方向发生变化时,活动将被销毁并与所有对象一起重新创建。发生这种情况时,会抛出IllegalStateException
。研究揭示了这个问题:适配器内容不应该从后台线程修改,而只能从UI线程修改。 see also here
在我看来TextWatcher是在后台线程上调用的,同时重新创建布局,过滤适配器内容并导致异常。
我该如何解决这个问题?
修改
我正在构建针对Android 1.6(Donut) - API Level 4并使用三星Galaxy S2,Android 2.3.3进行测试。
活动:
public class MyActivity extends ListActivity {
MyAdapter adapter;
EditText searchBox;
public boolean activeSearch = false;
public ArrayList<MyItem> filteredItems = new ArrayList<MyItem>();
/*
* Activity overrides
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
getListView().setTextFilterEnabled(true);
adapter = new MyAdapter(this, R.layout.myitemlayout);
setListAdapter(adapter);
searchBox = (EditText)findViewById(R.id.mySearchBox);
searchBox.addTextChangedListener(searchBoxTextWatcher); // defined below
}
@Override
protected void onDestroy() {
super.onDestroy();
searchBox.removeTextChangedListener(searchBoxTextWatcher);
}
/*
* Search box text watcher
*/
TextWatcher searchBoxTextWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence searchText, int start, int before, int count) {
adapter.getFilter().filter(searchText);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
};
}
适配器:
public class MyAdapter extends BaseAdapter implements Filterable {
MyActivity activity;
int layoutID;
Filter filter;
/*
* Constructor
*/
public MyAdapter(MyActivity activity, int layoutID) {
super();
this.activity = activity;
this.layoutID = layoutID;
}
/*
* BaseAdapter overrides
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
MyItem item = activity.activeSearch ? activity.filteredItems.get(position) : allItems.get(position);
// Create/prepare view
// ...
return view;
}
@Override
public int getCount() {
return activity.activeSearch ? activity.filteredItems.size() : allItems.size();
}
@Override
public MyItem getItem(int position) {
return activity.activeSearch ? activity.filteredItems.get(position) : allItems.get(position);
}
@Override
public long getItemId(int position) {
return getItem(position).itemID;
}
/*
* Filterable overrides
*/
@Override
public Filter getFilter() {
if (filter == null) {
filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
String searchText = constraint.toString();
FilterResults results = new FilterResults();
if (searchText == null || searchText.length() == 0) {
synchronized (this) {
activity.activeSearch = false;
results.values = allItems;
results.count = allItems.size();
}
} else {
synchronized (this) {
activity.activeSearch = true;
ArrayList<MyItem> filteredItems = new ArrayList<MyItem>();
ArrayList<MyItem> tmpItems = new ArrayList<MyItem>();
tmpItems.addAll(allItems);
for (MyItem item : tmpItems) {
boolean matched = false;
// Compare various properties of item against searchText
// and set matched = true if it matched.
// ...
if (matched) {
filteredItems.add(item);
}
}
results.values = filteredItems;
results.count = filteredItems.size();
}
}
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
activity.filteredItems = (ArrayList<MyItem>)results.values;
notifyDataSetChanged();
}
};
}
return filter;
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<EditText
android:id="@+id/mySearchBox"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="Enter something :)"
android:inputType="number"
android:maxLines="1"
></EditText>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white"
></ListView>
</LinearLayout>
堆栈跟踪:
Thread [<1> main] (Suspended (exception IllegalStateException))
ListView.layoutChildren() line: 1558
ListView(AbsListView).onLayout(boolean, int, int, int, int) line: 1384
ListView(View).layout(int, int, int, int) line: 7228
LinearLayout.setChildFrame(View, int, int, int, int) line: 1254
LinearLayout.layoutVertical() line: 1130
LinearLayout.onLayout(boolean, int, int, int, int) line: 1047
LinearLayout(View).layout(int, int, int, int) line: 7228
FrameLayout.onLayout(boolean, int, int, int, int) line: 338
FrameLayout(View).layout(int, int, int, int) line: 7228
PhoneWindow$DecorView(FrameLayout).onLayout(boolean, int, int, int, int) line: 338
PhoneWindow$DecorView(View).layout(int, int, int, int) line: 7228
ViewRoot.performTraversals() line: 1148
ViewRoot.handleMessage(Message) line: 1868
ViewRoot(Handler).dispatchMessage(Message) line: 99
Looper.loop() line: 123
ActivityThread.main(String[]) line: 3691
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 507
ZygoteInit$MethodAndArgsCaller.run() line: 847
ZygoteInit.main(String[]) line: 605
NativeStart.main(String[]) line: not available [native method]
LogCat显示没有Stacktrace,只有这个(不知道它是否相关):
09-15 09:23:59.575: WARN/WindowManager(2696): Window freeze timeout expired.
09-15 09:23:59.575: WARN/WindowManager(2696): Force clearing orientation change: Window{40a82ba8 InputMethod paused=false}
09-15 09:23:59.575: WARN/WindowManager(2696): Force clearing orientation change: Window{40bff2f8 my.package/my.package.Activities.MyActivity paused=false}
09-15 09:23:59.875: WARN/InputConnectionWrapper.ICC(2820): Timed out waiting on IInputContextCallback
09-15 09:24:20.330: WARN/InputConnectionWrapper.ICC(2820): Timed out waiting on IInputContextCallback
09-15 09:24:20.330: ERROR/InputMethodService(2820): Unexpected null in startExtractingText : mExtractedText = null, input connection = com.android.internal.view.InputConnectionWrapper@406a82c0
09-15 09:24:22.340: WARN/InputConnectionWrapper.ICC(2820): Timed out waiting on IInputContextCallback
09-15 09:24:22.340: ERROR/InputMethodService(2820): Unexpected null in startExtractingText : mExtractedText = null, input connection = com.android.internal.view.InputConnectionWrapper@406a82c0