一种优化 ListView 初始化加载速度的方案
Posted yinhuanxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一种优化 ListView 初始化加载速度的方案相关的知识,希望对你有一定的参考价值。
我在使用 ListView 的时候,有一个问题困扰我挺久:能不能控制 ListView 初始化时加载的Item数量?
Adapter的getCount方法?我一直以为getCount方法是用来告诉 Listview,Adapter有多少数据的,而ListView初始化时只加载一个屏幕的数据。那到底是这样吗?
为什么要控制 ListView 初始化时加载的Item数量?
比如,如果我刚打开一个页面,ListView关联Adapter就开始加载十几条数据,如果加载的Item是TextView还好,影响不大,但如果是Webview呢?有些业务是需要Webview来作为ListView的Item,这时候就卡爆了。
我对ListView不怎么熟悉,因为我刚开始学习android不久,Recyclerview就开始火了,经常用的也是RecyclerView,在学校时做的小作品也比较简单,在列表加载遇到的坑也比较少,现在工作了,不停的踩坑,发现要认真学一下ListView和RecyclerView才行。从实现原理入手。
接下来提出一种优化ListView的方案:
初始化ListView时,如果Item过于复杂,那么初始化时数据源应尽量小。
我以前从来就没有关心过初始化数据源大小的问题,现在从源码来分析。
ListView在初始化时,会调用setAdapter方法来关联Adapter。在setAdapter方法,又会调用到getCount方法,源码如下所示:
public void setAdapter(ListAdapter adapter)
//代码省略
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null)
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
requestLayout();
这个mItemCount字段很重要。
在之后ListView填充数据时,会调用fillDown方法:
private View fillDown(int pos, int nextTop)
View selectedView = null;
int end = (mBottom - mTop);
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK)
end -= mListPadding.bottom;
while (nextTop < end && pos < mItemCount)
// is this the selected item?
boolean selected = pos == mSelectedPosition;
View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
nextTop = child.getBottom() + mDividerHeight;
if (selected)
selectedView = child;
pos++;
setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
从这里可以知道,会进入一个while循环来填充数据。
又会调用到makeAndAddView方法,源码如下所示:
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected)
View child;
// Make a new view for this position, or convert an unused view if possible
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
在这里调用到了obtainView方法,obtainView在ListView的父类AbsListView里,源码如下所示:
iew obtainView(int position, boolean[] isScrap)
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null)
if (child != scrapView)
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
else
if (child.isTemporarilyDetached())
isScrap[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
else
// we set isScrap to "true" only if the view is temporarily detached.
// if the view is fully detached, it is as good as a view created by the
// adapter
isScrap[0] = false;
return child;
到这里已经可以看到Adapter的getView方法了,由此我们可以得出结论,fillDown方法确实是在填充数据。
回到fillDown方法,进入一个死循环来填充数据,而从循环的条件:pos < mItemCount 可以知道,getCount方法返回的数值多大,那么listView初始化时就要填充多少数据。
而通常我们在getCount方法都是这样实现的:
@Override
public int getCount()
return mData.size();
只要控制好初始化的数据源大小即可。
这是一种优化ListView初始化加载速度的方案。初始化数据源不宜过大,可以根据布局占屏幕的比例来定义。
全文完。
以上是关于一种优化 ListView 初始化加载速度的方案的主要内容,如果未能解决你的问题,请参考以下文章
ListView嵌套RecycleView滑动卡顿问题的优化方案
ListView嵌套RecycleView滑动卡顿问题的优化方案