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()。

相關文章
相關標籤/搜索