2

正気を保つために、ここに投稿しています。今は自分で解決できないと確信しているためです。過去にこの問題の解決策が得られなかったのは、すべてを投稿しなかったためだと思うので、知っておくべきことをすべて投稿しています。基本的に可能な限り軽量になるように、これまでコードを削除しました。しかし、それでもこのエラーが発生します (非常にまれですが):

03-27 10:57:27.783: E/java.lang.IllegalStateException(4929): 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(16908298, class android.widget.ListView) with Adapter(class de.innosoft.android.mobileserviceclient.activities.ListEinsaetze$ArrayAdapterEinsatz)]
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929): 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(16908298, class android.widget.ListView) with Adapter(class de.innosoft.android.mobileserviceclient.activities.ListEinsaetze$ArrayAdapterEinsatz)]
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.ListView.layoutChildren(ListView.java:1544)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.AbsListView.onLayout(AbsListView.java:1994)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.AdapterView.updateEmptyStatus(AdapterView.java:747)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.AdapterView.checkFocus(AdapterView.java:720)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:812)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:5958)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at de.innosoft.android.mobileserviceclient.ui.DynamicArrayAdapter.notifyDataSetChanged(DynamicArrayAdapter.java:291)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at de.innosoft.android.mobileserviceclient.ui.DynamicArrayAdapter.notifyDataSetChanged(DynamicArrayAdapter.java:268)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at de.innosoft.android.mobileserviceclient.ui.DynamicArrayAdapter$DynamicAdapterFilter.publishResults(DynamicArrayAdapter.java:119)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:282)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.os.Handler.dispatchMessage(Handler.java:99)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.os.Looper.loop(Looper.java:137)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at android.app.ActivityThread.main(ActivityThread.java:5039)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at java.lang.reflect.Method.invokeNative(Native Method)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at java.lang.reflect.Method.invoke(Method.java:511)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-27 10:57:27.783: E/java.lang.IllegalStateException(4929):    at dalvik.system.NativeStart.main(Native Method)

この投稿の一番下で、このエラーがいつ発生するかを説明します。でもまずはコード。私はこのTextWatcherを持っています:

edSearch.addTextChangedListener(new TextWatcher() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }

                String text = null;

                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    text = s.toString();
                }

                @Override
                public void afterTextChanged(final Editable s) {
                    if (text != null && !text.equals(s.toString())) {
                        adapter.getFilter().filter(s.toString());
                    }
                    text = null;
                }
            });

このスレッドはサイドラインで実行され、200 ミリ秒ごとに textwatcher をトリガーします。これを手動でテストするのはクレイジーです:

new Thread(new Runnable() {
            public void run() {
                Runnable r = new Runnable() {
                    public void run() {
                         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
                         KeyEvent.KEYCODE_S));
                         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
                         KeyEvent.KEYCODE_S));
                    }
                };

                Runnable r2 = new Runnable() {
                    public void run() {
                         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
                         KeyEvent.KEYCODE_DEL));
                         dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
                         KeyEvent.KEYCODE_DEL));
                    }
                };
                while (true) {

                    runOnUiThread(r);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        ExceptionHandler.handle(e);
                    }
                    runOnUiThread(r2);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        ExceptionHandler.handle(e);
                    }
                }
            }
        }).start();
    }

これは私のアダプターです:

private class ArrayAdapterEinsatz extends DynamicArrayAdapter<Einsatz> {

        public ArrayAdapterEinsatz(int textViewResourceId) {
            super(ListEinsaetze.this, textViewResourceId, new ArrayList<Einsatz>());
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {

            Einsatz einsatz = getFilteredItems().get(position);

            View rowView = convertView;
            if (rowView == null) {
                LayoutInflater inflater = ListEinsaetze.this.getLayoutInflater();
                rowView = inflater.inflate(R.layout.eintrag_einsatz, null);
                ViewHolder viewHolder = new ViewHolder();

                viewHolder.tvId = (TextView) rowView.findViewById(R.id.eeinsatzid);
                viewHolder.tvDatumVonbis = (TextView) rowView.findViewById(R.id.eeinsatzvonbis);
                viewHolder.tvKundenName = (TextView) rowView.findViewById(R.id.eeinsatzkundenname);
                viewHolder.tvKundenOrt = (TextView) rowView.findViewById(R.id.eeinsatzort);
                viewHolder.tvEntfernung = (TextView) rowView.findViewById(R.id.eeinsatzentfernung);
                viewHolder.iveRotate = (ImageViewEffects) rowView.findViewById(R.id.eeinsatzrotate);
                viewHolder.iveRotate.setBitmap(Utils.drawableToBitmap(R.drawable.ic_refresh_1, 22), ImageViewEffects.ROTATE);
                viewHolder.ivAnsprechpartnerVorhanden = (ImageView) rowView.findViewById(R.id.eeinsatzimageansprechvorhanden);
                viewHolder.ivBelegeVorhanden = (ImageView) rowView.findViewById(R.id.eeinsatzimagebelegevorhanden);
                viewHolder.ivTaetigkeitVorhanden = (ImageView) rowView.findViewById(R.id.eeinsatzimagetaetigkeitvorhanden);

                rowView.setTag(viewHolder);
            }

            final ViewHolder holder = (ViewHolder) rowView.getTag();

            return rowView;
        }

    }

最後に、これは基本アダプター クラスです。一番下にある実際のフィルター関数に注目してください。これは new Random().nextBoolean() に従ってフィルター処理されますが、これはこれ以上単純ではありません。

public class DynamicArrayAdapter<T> extends BaseAdapter implements Filterable {

  public class DynamicAdapterFilter extends Filter {

    private CharSequence m_Filter = null;
    private List<T> m_FilteredItems = null;

    public DynamicAdapterFilter() {
      m_FilteredItems = new ArrayList<T>();
    }

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {


      FilterResults r = new FilterResults();
      List<T> items = null;
      m_Filter = constraint;

      if (constraint == null) {
        items = m_AllItems;
      } else {
        items = m_FilteredItems;
        items.clear();

        synchronized (SyncLock) {
          List<T> l = new ArrayList<T>(m_AllItems);
          for (T item : l) {
            if (DynamicArrayAdapter.this.filter(item, constraint)) {
              items.add(item);
            }
          }
        }
      }

      r.values = items;
      r.count = items.size();

      return r;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {

      m_Items = (List<T>) results.values;

      notifyDataSetChanged();

    }

    public void refresh() {
      if (m_Filter != null) {

        filter(m_Filter);
      }
    }

    public void refresh(FilterListener listener) {
      if (m_Filter != null ) {

        filter(m_Filter, listener);
      }
    }
  }


  private final Object SyncLock = new Object();
  private Context m_Context = null;
  private LayoutInflater m_Inflater = null;
  private int m_DelegateResourceId;
  private boolean m_ChangeNotificationsEnabled = true;
  private List<T> m_AllItems = null;
  private List<T> m_Items = null;
  private DynamicAdapterFilter m_Filter = null;


  public DynamicArrayAdapter(Context context, int delegateResourceId) {
    this(context, delegateResourceId, new ArrayList<T>());
  }

  public DynamicArrayAdapter(Context context, int delegateResourceId, List<T> items) {
    m_Context = context;
    m_DelegateResourceId = delegateResourceId;
    initItems(items);

    m_Inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  }

  public void initItems(List<T> items) {
    m_AllItems = m_Items = items;
  }

  /**
   * returns the filtered item count (or all item count if no filter exists)
   */
  @Override
  public int getCount() {
        return m_Items != null ? m_Items.size() : 0;
  }

  /**
   * returns the item at the position in the filtered list (or all item count if
   * no filter exists)
   */
  @Override
  public T getItem(int position) {
    return m_Items.get(position);
  }

  /**
   * holt position von item, exakte gleiche implementation wie ArrayAdapter<T>
   * 
   * @param item
   * @return
   */
  public int getPosition(T item) {
    return m_Items.indexOf(item);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public long getItemId(int position) {

    return position;
  }

  /**
   * returns the list view item delegate view. this view is inflated if it isn't
   * already inflated. Override this method to perform custom view operations
   * for the list view item.
   */
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
      convertView = m_Inflater.inflate(m_DelegateResourceId, parent, false);
    }

    return convertView;
  }

  /**
   * returns the filter. this filter is created if it hasn't yet been created.
   */
  @Override
  public DynamicAdapterFilter getFilter() {
    if (m_Filter == null) {
      m_Filter = new DynamicAdapterFilter();
    }

    return m_Filter;
  }

  /**
   * If notifications are enabled, this notifies the base adapter that the
   * backing data has changed
   */
  @Override
  public void notifyDataSetChanged() {
    notifyDataSetChanged(false);
  }

  /**
   * If forced or notifications are enabled, this notifies the base adapter that
   * the backing data has changed
   * 
   * @param force
   *          force notify even if notifications are disabled
   */
  public void notifyDataSetChanged(boolean force) {
    if (force || m_ChangeNotificationsEnabled) {

      super.notifyDataSetChanged();
    }
  }

  /**
   * @return UI context
   */
  public Context getContext() {
    return m_Context;
  }

  /**
   * @return UI inflater
   */
  public LayoutInflater getLayoutInflater() {
    return m_Inflater;
  }

  /**
   * @return Layout ID of the list view item delegate
   */
  public int getDelegateResourceId() {
    return m_DelegateResourceId;
  }

  /**
   * @return true if notifications are enabled, false otherwise
   */
  public boolean getChangeNotificationsEnabled() {
    return m_ChangeNotificationsEnabled;
  }

  /**
   * Set whether notifications are enabled or not
   */
  public void setChangeNotificationsEnabled(boolean value) {
    m_ChangeNotificationsEnabled = value;
  }

  // internal mechanism to handle automagical updates of the list items
  private void onDataChange() {


    if (m_Filter == null) {
      notifyDataSetChanged();
    } else {
      m_Filter.refresh();
    }
  }

  /**
   * @return All (unfiltered) items
   */
  public List<T> getAllItems() {
    return m_AllItems;
  }

  /**
   * Set the backing list
   */
  public void setAllItems(List<T> value) {
    m_AllItems = value;

    onDataChange();
  }

  /**
   * @return Current filtered items, or all items if there is no filter
   */
  public List<T> getFilteredItems() {
    return m_Items;
  }

  public void add(T item) {
    synchronized (SyncLock) {
      m_AllItems.add(item);
    }

    onDataChange();
  }

  public void insert(T item, int index) {
    synchronized (SyncLock) {
      m_AllItems.add(index, item);
    }

    onDataChange();
  }

  public void remove(T item) {
    synchronized (SyncLock) {
      m_AllItems.remove(item);
    }

    onDataChange();
  }

  public void removeAt(int index) {
    synchronized (SyncLock) {
      m_AllItems.remove(index);
    }

    onDataChange();
  }

  public void clear() {
    synchronized (SyncLock) {
      m_AllItems.clear();
    }

    onDataChange();
  }

  public void sort(Comparator<? super T> comparator) {
    synchronized (SyncLock) {
      Collections.sort(m_Items, comparator);
    }

    onDataChange();
  }

  protected boolean filter(T item, CharSequence constraint) { // AR
    return new Random().nextBoolean();

  }
}

したがって、基本的に、プログラムは 200 ミリ秒ごとに textwatcher をトリガーするだけです。そのため、textwatcher はリストを何度もフィルタリングします。プログラムでは、他に何も起こっていません。

ブレークポイントを設定しないと、プログラムは常にエラーなしで正常に実行されます。

行をブレークポイントするとエラーが発生します

adapter.getFilter().filter(s.toString());

TextWatcher で F8 (続行) を押したままにします (これはプログラム フローに不規則性をもたらすようですが、想像することしかできません)。ブレークポイントがなければエラーは発生しません。

ところで、私が置くと

adapter.notifyDatasetChanged()

adapter.getFilter().filter(s.toString());

同じエラーが 100% 発生します (notifyDatasetChanged が publishResults で呼び出されることはわかっているので、必要ありませんが、それでも正しい動作ですか?)。

TL;DR:

コード (特に基本アダプター コード) はフェイルセーフですか? (それ以外の場合、エラーは発生しません)

編集:

ブレークポイントが設定されていない場合にもエラーが発生します。

4

2 に答える 2

0

移動してみてください

 adapter.getFilter().filter(s.toString());

runnable で view.post() を使用して ui スレッドに入力します。

それはあなたの問題を解決する「かもしれません」。

于 2013-03-27T11:30:24.343 に答える
0

代わりに、以下のようにアダプターを編集テキストに設定するだけで、テキストウォッチャーを追加しないでください。

edSearch.setAdapter(アダプター);

また、public Filter getFilter() および protected void publishResults(CharSequence contraint, FilterResults results)
メソッドをオーバーライドします。

これはあなたの問題を処理します..同じ問題に直面し、この方法で解決しました:)

于 2013-03-28T08:04:13.660 に答える