您的位置:首页 > 其它

一种优化 ListView 初始化加载速度的方案

2017-08-08 16:54 399 查看
我在使用 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初始化加载速度的方案。初始化数据源不宜过大,可以根据布局占屏幕的比例来定义。

全文完。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: