ListView复用机制和复用问题解决的自我总结

核心RecycleBin

画面展示流程

1. ListView展示会经历 两次onMeasure && 两次 onLayout

2. 第一次的onLayout中,具体是在LayoutChildren,最终通过getView(xxx,null,listView)获得childView,addViewInLayout方式添加到ListView。

此时activeViews(View数组),记录的是所有屏幕上显示的views。

3. 第二次的onLayout中,具体是在LayoutChildren,因为childCount>0,for循环将view添加到activeViews(View数组),然后把所有的view Detach。最终在

fillDown中,优先从activeViews中获取(获取后View数组应该已经空了),获取到后addViewInParents。

👉至此,ListView首次展示完成。

 

滑动时体现复用

进行滑动 判断,若view滑出屏幕,则将View废弃缓存到ScrapViews(无序存储)。

view滑动到屏幕,最终会先从scrapView中获取View进行复用, 也就是 getView(xxx,scrapView,listView)。

👉最终其实inflate出来的itemView其实只有屏幕所展示数量多一点,大多数时候,是一直在进行复用的。

 

滑动复用出现的问题(图片异步加载乱序)

但是异步加载图片后,会发现,图片先显示了复用的View的旧图,之后才展示了本应该展示的图片。

👉解决方法(常用方法):

getView中拿到listView实例对象(getView的第三个参数);getView中为当前的imageView设置tag,tag为其所应该展示的url(可作为代表的数据),在异步加载成功处,调用findViewWithTag(url)获取应该展示的imageView对象,并设置drawable。

 

有不对的,请帮忙纠正。

记忆衰退厉害的我只能默默总结记录下来。

 

 

详细代码就不贴了,主要就是围绕ListView的layoutChildren,onTouchEvent。

仅附上RecycleBin的核心代码(fillActiveViews && addScrapView)

class RecycleBin {
	private RecyclerListener mRecyclerListener;
 
	private int mFirstActivePosition;
 
	private View[] mActiveViews = new View[0];
 
	private ArrayList<View>[] mScrapViews;
 
	private int mViewTypeCount;
 
	private ArrayList<View> mCurrentScrap;
    
    // 保存view到mActiveViews中
	void fillActiveViews(int childCount, int firstActivePosition) {
		if (mActiveViews.length < childCount) {
			mActiveViews = new View[childCount];
		}
		mFirstActivePosition = firstActivePosition;
		final View[] activeViews = mActiveViews;
		for (int i = 0; i < childCount; i++) {
			View child = getChildAt(i);
			AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
			// Don't put header or footer views into the scrap heap
			if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
				// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in
				// active views.
				// However, we will NOT place them into scrap views.
				activeViews[i] = child;
			}
		}
	}
 
 
    // View滑出屏幕时候,将View废弃缓存到mScrapViews中
	void addScrapView(View scrap) {
		AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
		if (lp == null) {
			return;
		}
		// Don't put header or footer views or views that should be ignored
		// into the scrap heap
		int viewType = lp.viewType;
		if (!shouldRecycleViewType(viewType)) {
			if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
				removeDetachedView(scrap, false);
			}
			return;
		}
		if (mViewTypeCount == 1) {
			dispatchFinishTemporaryDetach(scrap);
			mCurrentScrap.add(scrap);
		} else {
			dispatchFinishTemporaryDetach(scrap);
			mScrapViews[viewType].add(scrap);
		}
 
		if (mRecyclerListener != null) {
			mRecyclerListener.onMovedToScrapHeap(scrap);
		}
	}
 
}

参考文章:

https://blog.csdn.net/guolin_blog/article/details/44996879

https://blog.csdn.net/guolin_blog/article/details/45586553

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值