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

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android ListView 实现商品倒计时时,常见的时间错乱问题是由于 ListView 的视图复用机制导致的。当 ListView 滑动时,由于视图的复用,导致原本应该显示的倒计时时间被重置,出现时间错乱的问题解决这个问题的方法是在 getView 方法中对倒计时时间进行保存和更新。具体实现如下: 1. 在 Bean 类中添加一个 long 类型的字段用于保存商品的倒计时时间戳: ``` public class GoodsBean { ... private long countdownTime; // 倒计时时间戳 ... // getter 和 setter 方法 } ``` 2. 在 Adapter 的 getView 方法中对倒计时时间进行保存和更新。首先获取当前商品的倒计时时间,如果该商品的倒计时时间戳已经保存,则直接使用保存的时间戳进行倒计时更新;否则,根据商品的截止时间计算倒计时时间戳并保存到商品的 countdownTime 字段中: ``` public View getView(int position, View convertView, ViewGroup parent) { ... GoodsBean goods = getItem(position); long countdownTime = goods.getCountdownTime(); if (countdownTime == 0) { countdownTime = calculateCountdownTime(goods.getEndTime()); // 根据商品的截止时间计算倒计时时间戳 goods.setCountdownTime(countdownTime); // 保存倒计时时间戳 } ... TextView tvCountdown = convertView.findViewById(R.id.tv_countdown); updateCountdown(tvCountdown, countdownTime); // 更新倒计时显示 ... } private void updateCountdown(final TextView tvCountdown, long countdownTime) { ... // 倒计时更新逻辑 ... } ``` 通过这样的方式,可以在 ListView 的视图复用过程中正确地保存和更新商品的倒计时时间,解决倒计时错乱的问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值