adapter has changed but ListView did not receive a notification

完整的异常信息如下:
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(2131756587, class com.xxx.xlistview) with Adapter(class android.widget.HeaderViewListAdapter)]

思考异常的来源:
首先要想一下这是哪个类里面抛出来的异常信息,根据上面的提示无非就2个类listview(xlistview是继承与listview)和HeaderViewListAdapter,于是就点到这2个类的源码里面看一下嘛,对不对,直接ctrl+F搜索异常信息进行定位,在listview就能找到异常的信息具体信息,在layoutChildren()方法中,如下源码所示:

  @Override
    protected void layoutChildren() {
        final boolean blockLayoutRequests = mBlockLayoutRequests;
        if (blockLayoutRequests) {
            return;
        }
        mBlockLayoutRequests = true;
         ......  此处代码省略          
            // Handle the empty set by removing all views that are visible
            // and calling it a day
            if (mItemCount == 0) {
                resetList();
                invokeOnItemScrollListener();
                return;
            } 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() + ")]");
            }

            setSelectedPositionInt(mNextSelectedPosition);

分析异常的原因
根据上面的源码我们知道是下面的判断条件不满足导致的

mItemCount != mAdapter.getCount()

mAdapter.getCount()很明显是mAdapter数据mList.size()的大小,那么mItemCount 又是什么呢
于是又在listView中ctrl+F搜索

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Sets up mListPadding
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         ......  此处代码省略 
        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
        if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
                || heightMode == MeasureSpec.UNSPECIFIED)) {
            final View child = obtainView(0, mIsScrap);

在其他地方搜索到的再结合上面的代码,我们大致可以知道mItemCount 是指ListView缓存的数据Count
举个例子:

if (mDemoAdapter == null) {
         mDemoAdapter = new DemoAdapter(mDataList);
     }
 lv.setAdapter(mDemoAdapter );

mDemoAdapter对应的数据为mDataList,假如说第一次打开某个页面的数据mDataList的大小为20,即是 mAdapter.getCount()=20 mItemCount =20,然后做了一次下拉刷新,我们对下拉刷新的数据集合mDataList做了一系列的增删改操作后mDataList的大小为23,注意此时mDemoAdapter还没有notification,后面还跟了一系列的 其他操作,那么此时listView的mItemCount 还是20, 20!=23这时候就会报上面的错误

解决办法

方法一:

你对mDataList进行增加、删除的时候,一定要同步进行notifyDataSetChanged()方法来通知listview更新(注意:更新要在主线程中进行)。

方法二:
因为方法一在有些场景中不满足条件例如: 我不想每次增加/删除一个条目就在界面上反应出来,而是把所有的增删改都做完后,再在界面上出现,很显然方法一就不满足,于是就有了下面这个方法,也是我个人比较推荐的方法;

 if (mDataList == null) {
        mDataList = new ArrayList();
         //多添加一个集合,用于增删改查操作,等所有的操作都做完,
         //再 mDataList.addAll(tempList);再通知更新
         tempList=new ArrayList();;
  }
  if (mDemoAdapter == null) {
         mDemoAdapter = new DemoAdapter(mDataList);
   }
   lv.setAdapter(mDemoAdapter );
 mDataList.addAll(tempList);
 mDemoAdapter .setRefreshListData(mDataList);
 // tempList也可以放在从后台解析出数据之后先tempList.clear(),在tempList.add(mDemoBean)
 tempList.clear();

你可能感兴趣的:(android常见问题)