一种优化 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优化方案

ListView嵌套RecycleView滑动卡顿问题的优化方案

ListView嵌套RecycleView滑动卡顿问题的优化方案

基于Android官方AsyncListUtil优化经典ListView分页加载机制

网页优化方案

前端页面加载速度优化解决方案