ListView嵌套ListView缓存失效问题

ListView嵌套ListView缓存失效问题

Android中在写列表的时候,相信不少时候,咱们都须要进行ListView嵌套ListView编程。好比说:帖子+评论页面的编写。然而这种模型是会出现被嵌套ListView缓存失效的问题。编程

被嵌套的ListView的代码为:缓存

public class NestListView extends ListView {
	public NestListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public NestListView(Context context) {
		super(context);
	}

	public NestListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//无限大小的子View空间
		int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
		super.onMeasure(widthMeasureSpec, expandSpec);
	}
}

最近,在写微信朋友圈的功能中,由于须要处理说说和评论,**因此采用ListView 嵌套ListView的处理模式。**而不幸的是,发生了评论的ListView的View缓存没法使用的状况,从而形成GC比较频繁。微信

CommentAdapter adapter = (CommentAdapter) lv_lc_comment.getAdapter();
if (adapter == null) {
	adapter = new CommentAdapter(context);
	lv_lc_comment.setAdapter(adapter);
}
//从新设置数据
adapter.setData(cmTopicCommentList);
adapter.setClsId(cmTopic.getClsId());
adapter.setTag(tag);
adapter.setOnClickListener(onTopicListener);
adapter.notifyDataSetChanged();

上面这段代码就是评论adapter刷新代码。在实际运行过程当中,会发生View Cache没法使用的问题。ide

调试代码,开始是怀疑不能屡次调用setAdapter的问题**(以前都是没有添加adapter是否存在判断)**,而setAdapter代码为:调试

public void setAdapter(ListAdapter adapter) {
	...
	mRecycler.clear();
	...
}

能够发现,setAdapter()会清空缓存。而采用最新的代码编写后,仍是发生了View Cache无效的问题。再次翻阅ListView的代码AbsListView# obtainView :code

View obtainView(int position, boolean[] isScrap) {
	...
	//查看是否存在相应的View缓存
	scrapView = mRecycler.getScrapView(position);
	...

	return child;
}

可见,在ListView显示的时候,会去查询RecycleBin中是否存在对应的View缓存的,可是每次都是为null。get

可是业务逻辑中,刷新一条说说的时候,会总体刷新它的评论,因此按照正常的逻辑来讲,被嵌套的评论ListView中,应该包含了以前的评论View缓存。这必定是哪里出了问题。it

因此看了一下**RecycleBin回收View的各个时机,**而后发现:io

protected void layoutChildren() {
	...
		//若是数据变化,则回收View
		if (dataChanged) {
			for (int i = 0; i < childCount; i++) {
				recycleBin.addScrapView(getChildAt(i), firstPosition+i);
				if (ViewDebug.TRACE_RECYCLER) {
					ViewDebug.trace(getChildAt(i),
							ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
				}
			}
		} else {
			recycleBin.fillActiveViews(childCount, firstPosition);
		}
	....
}

翻看了几个addScrapView几个被调用的地方后,发现layoutChildren这个调用最可疑,由于它利用dataChanged标志来判断是否回收View,而 notifyDataSetChanged就是标记须要回收View。class

而且ListView#onMeasure->ListView#measureHeightOfChildren() 须要进行获取子View高度的时候:

final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,

	...

	for (i = startPosition; i <= endPosition; ++i) {
		child = obtainView(i, isScrap);

		measureScrapChild(child, i, widthMeasureSpec);

		...
	}
	...
}

**因此应该是NestListView这种无限长度的ListView的锅:**由于在onMeasure的时候obtainView全部的View高度,而RecycleBin是在onLayout的时候才能缓存不使用的View。而View#onMeasure() > View#onLayout(), 因此形成被嵌套的ListView缓存失效的问题。

而最新的RecycleView已经能够支持View强制回收了API为:swapAdapter()。

相关文章
相关标签/搜索