在TextWatcher中过滤适配器时出现IllegalStateException

时间:2011-09-14 14:51:47

标签: android exception adapter illegalstateexception textwatcher

我有一个由自定义适配器(扩展ListActivity)支持的自定义BaseAdapterInspired by this,布局还包含EditTextTextWatcher。适配器实现FilterableTextWatcher.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

0 个答案:

没有答案