完整的异常信息如下:
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();