核心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