android - 适配器的内容已更改但ListView未收到通知

时间:2014-10-23 05:10:49

标签: android android-listview android-asynctask

我收到此错误: java.lang.IllegalStateException:适配器的内容已更改,但ListView未收到通知。确保不从后台线程修改适配器的内容,而只是从UI线程修改。 [在ListView(2131034188,类android.widget.ListView)中使用Adapter(类.MainActivity $ ListAdapter)]

这就是我所做的,我运行AsyncTask并获取json数据,然后,onPostExecute,我调用ListAdapter将数据转换为listview。

@Override
        protected Void doInBackground(Void... arg0) {
            Spots_tab1_json sh = new Spots_tab1_json();
            String jsonStr = sh.makeServiceCall(url + page, Spots_tab1_json.GET);

            if (jsonStr != null) {
JSONObject jsonObj = new JSONObject(jsonStr);
                    contacts = jsonObj.getJSONArray(TAG_CONTACTS);
for (int i = 0; i < contacts.length(); i++) {
                        JSONObject c = contacts.getJSONObject(i);
                        String onvan = new String(c.getString("onvan").getBytes("ISO-8859-1"), "UTF-8");
                        String id = new String(c.getString("id").getBytes("ISO-8859-1"), "UTF-8");
                        String dates = new String(c.getString("dates").getBytes("ISO-8859-1"), "UTF-8");
                        String price = new String(c.getString("gheymat").getBytes("ISO-8859-1"), "UTF-8");
                        HashMap<String, String> contact = new HashMap<String, String>();
                        contact.put("onvan", onvan);
                        contact.put("img", new String(c.getString("img").getBytes("ISO-8859-1"), "UTF-8"));
                        contactList.add(contact);
                    }
}
}
} 

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
            if (!isCancelled() && goterr == false) {
            final ListAdapter ladap=new ListAdapter(MainActivity.this, contactList);
            lv.setAdapter(ladap);
            runOnUiThread(new Runnable() {
                public void run() {
                    ladap.notifyDataSetChanged();
                }});
        }
    }

然后,我的listView被构建。我已经在几个不同的设备上测试了它,没有它们有任何问题,但有些用户告诉我,我登录并看到这个错误。

我该怎么做才能解决它?怎么了 ?

谢谢你

2 个答案:

答案 0 :(得分:7)

对于迟来的回复道歉,但在我之前的评论中显示为怀疑:您正在从两个不同的主题修改contactList

ListView缓存元素计数,每当必须布局子元素时,它将再次检查绑定适配器中当前项目数的计数。如果计数已更改且未通知ListView,则会引发错误:

else if (mItemCount != mAdapter.getCount()) {
    throw new 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. Make sure your adapter calls notifyDataSetChanged() "
    + "when its content changes. [in ListView(" + getId() + ", " + getClass()
    + ") with Adapter(" + mAdapter.getClass() + ")]");
}

Source

换句话说:这正是你得到的错误。在您的方案中,计数差异是由多个线程修改后备数据集引起的,导致同步问题:后台线程可能会修改数据集,被挂起,然后ui线程可能会调用{{1}没有得到有关数据集更改的通知。

现在,解决方案。最简单的方法是确保您不从不同的线程修改绑定到layoutChildren()的适配器的列表。也就是说,要么制作一个可以自由修改的副本,要么分配一个新的列表。

所以,做这样的事情,即在ListView的{​​{1}}方法中。

新列表:

onPreExecute()

(浅)副本:

AsyncTask

然后,在List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(); 中,将数据添加到List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(contactList); 。最后,在doInBackground()中,您可以:

  1. 使用mNewContactList创建新适配器并将其设置为onPostExecute()
  2. 或者(如果您没有复制原始列表)将mNewContactList的内容添加到现有ListView并在mNewContactList上致电contactList

答案 1 :(得分:0)

数据更新和您的适配器调用notifyDataSetChanged()必须在同一代码块中。 例如:

正确的情况:

List

错误的情况:

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mSelectedImages.add(image);
                mAdapter.notifyDataSetChanged();
            }
        });