ListActivity
カスタム アダプター (拡張) に裏打ちされたカスタムがありBaseAdapter
ます。これに触発されて、レイアウトには と も含まれてEditText
いTextWatcher
ます。アダプターは を実装しFilterable
ます。Hamyが他の質問で提案したように、単純TextWatcher.onTextChanged(...)
に呼び出します。adapter.getFilter().filter(string);
問題:デバイスの向きが変わると、アクティビティが破棄され、すべてのオブジェクトと共に再作成されます。その場合、 anIllegalStateException
がスローされます。調査の結果、アダプターのコンテンツはバックグラウンド スレッドから変更するのではなく、UI スレッドからのみ変更する必要があるという問題が明らかになりました。こちらもご覧ください
私には、TextWatcher がバックグラウンド スレッドで呼び出され、レイアウトを再作成し、アダプターの内容をフィルタリングして例外を引き起こしているようです。
どうすればそれを回避できますか?
編集:
Android 1.6 (ドーナツ) - API レベル 4 に対してビルドし、Samsung 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