ListView優(yōu)化

視圖

  • convertView
    系統(tǒng)在繪制ListVIew時,針對每個Item都會調(diào)用一次getView()方法,該方法實(shí)現(xiàn)如下:
//寫法1
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v;
    v = mInflater.inflate(mLayoutId, parent, false);
    ItemInfo typeInfo = getItem(position);
    TextView tv = (TextView) v.findViewById(R.id.title);
    ImageView iv = (ImageView) v.findViewById(R.id.pic);
    tv.setText(typeInfo.typeTitle);
    iv.setImageResource(typeInfo.typePic);

    return v;
}
//---------------------------------------------------------------------------------------
//寫法2
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    View v;
    ViewHolder viewHolder;
    ItemInfo item= getItem(position);
    if(convertView == null){
        v = mInflate.inflate(mLayoutId, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.tv = (TextView) v.findViewById(R.id.title);
        viewHolder.iv = (ImageView) v.findViewById(R.id.pic);
        v.setTag(viewHolder);
    }else{
        v = convertView;
        viewHolder = (ViewHolder) v.getTag();
    }
    viewHolder.tv.setText(item.typeTitle);
    viewHolder.iv.setImageResource(item.typePic);
    
    return v;
}
class ViewHolder{
    TextView tv;
    ImageView iv;
}

觀察上述兩種寫法,如果我們不采用convertView的話,Adapter擁有多少個數(shù)據(jù)就需要Inflate多少個Layout這是非常消耗資源的。而對于ListView而言,同時只需持有屏幕上能顯示的最大條目數(shù)的View(n條)即可。通過復(fù)用convertView的方式,只需執(zhí)行n次inflate即可,之后則可以只做數(shù)據(jù)更新操作。
PS: 屏幕上最大能顯示的條目數(shù)可能不是剛開始顯示在屏幕上的完整條目數(shù),可能頂部顯示半個item,底部顯示半個item,此時顯示的條目數(shù)才是最多的。

  • ViewHolder
    優(yōu)化代碼如上所示,和復(fù)用convertView類似。我們在創(chuàng)建view的同時創(chuàng)建一個ViewHolder對象并執(zhí)行findViewById()對其成員進(jìn)行初始化,然后通過View類的setTag()方法將該ViewHolder對象與view綁定。當(dāng)我們需要更新數(shù)據(jù)時,通過View類的getTag()的方法獲取與控件綁定的ViewHolder對象,然后就可以得到需要更新數(shù)據(jù)的控件ID,這樣就可以減少對findViewById的調(diào)用,優(yōu)化數(shù)據(jù)更新時的效率。

數(shù)據(jù)

  • 分批加載
    假設(shè)我們擁有的數(shù)據(jù)源是一個包含有幾萬條數(shù)據(jù)的List,此時如果在ListView創(chuàng)建時一次性就將所有的數(shù)據(jù)加載進(jìn)來,就可能造成程序卡頓等現(xiàn)象。此時我們可以通過分批加載的方式來加載數(shù)據(jù):比如屏幕上最多可以顯示8條數(shù)據(jù),在初始化時我們一次加載20條數(shù)據(jù)。當(dāng)用戶將listview滾動到最底部時,程序執(zhí)行加載數(shù)據(jù)操作,再次加載50條數(shù)據(jù),并更新Adapter,這樣用戶單次等待時間就是加載50條數(shù)據(jù)的時間而不是幾萬條數(shù)據(jù)的時間。
    實(shí)現(xiàn)分批加載的重要實(shí)現(xiàn)代碼如下:
//maxDataSize:數(shù)據(jù)源包含的總數(shù)據(jù)條目
//isLoading:用于判斷是否處于load狀態(tài)
//mData:數(shù)據(jù)源數(shù)據(jù)
//mFooterView:用于顯示加載中的信息提示
//---------------------------------------------------------------------------------------
/**一次加載50條數(shù)據(jù)
 * @param totalCount 當(dāng)前數(shù)據(jù)的條目數(shù)
 */
private ArrayList<ItemInfo> getNewData(int totalCount) {
    ArrayList<ItemInfo> data = new ArrayList<ItemInfo>();
    //如果加載的條目剩余不足50條
    if(totalCount + addCount > maxDataSize){
        for(int i = totalCount; i < maxDataSize; i++)
            data.add(new ItemInfo("這是第"+i+"條列表項", R.drawable.ic_launcher));
    }else{
        for(int i = 1; i <= 50; i++)
            data.add(new ItemInfo("這是第"+(i+totalCount)+"條列表項", R.drawable.ic_launcher));
    }
    return data;
}
//對ListView設(shè)立Scroll事件監(jiān)聽,并重寫onScroll方法
@Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, final int totalItemCount) {
    // TODO Auto-generated method stub
    Log.d("Optimize", "first:"+ firstVisibleItem+" visible:"+ visibleItemCount
            + " total:" + totalItemCount);
    int lastItemId = view.getLastVisiblePosition();
    //已經(jīng)顯示了最后一條數(shù)據(jù)
    if(lastItemId == totalItemCount -1){
        if(totalItemCount >= maxDataSize){
            Toast.makeText(this, "沒有更多的數(shù)據(jù)了", Toast.LENGTH_SHORT).show();
        }
        //數(shù)據(jù)還沒有完全加載完,并且不處于load狀態(tài)
        else if(!isLoading){
            mFooterView.setVisibility(View.VISIBLE);
            new Thread(new Runnable(){

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    isLoading = true;
                    final ArrayList<ItemInfo> newData = getNewData(totalItemCount-1);
                    //這里采用延時的方式是為了模擬數(shù)據(jù)加載的過程,假設(shè)數(shù)據(jù)加載用了3s
                    mHandler.postDelayed(new Runnable(){

                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            //adapter的數(shù)據(jù)更新必須放在UI Thread中否則可能會報這樣的錯誤:
                            //java.lang.IllegalStateException: 
                            //The content of the adapter has changed but ListView did not receive a notification. 
                            mData.addAll(newData);
                            mAdapter.notifyDataSetChanged();
                            if(mListView.getFooterViewsCount() != 0)
                                mFooterView.setVisibility(View.GONE);
                            isLoading = false;
                        }
                        
                    }, 3000);
                }
                
            }).start();
        }
    }
}
  • 分頁加載
    采用分批加載的方式主要是優(yōu)化了ListView加載速度,而假設(shè)我們擁有幾百萬條數(shù)據(jù),如果我們只是單純的執(zhí)行分批加載,到最后我們的Adapter將持有一個擁有幾百萬條數(shù)據(jù)的List,這將占用極大的內(nèi)存??!
    此時我們可以通過分頁加載的方式來優(yōu)化內(nèi)存占用。分頁加載和分批加載類似,只是在加載第二批數(shù)據(jù)時對第一批數(shù)據(jù)進(jìn)行了清除操作。
    分頁加載的重要實(shí)現(xiàn)代碼如下:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, final int totalItemCount) {
    // TODO Auto-generated method stub
    Log.d("Optimize", "first:"+ firstVisibleItem+" visible:"+ visibleItemCount
            + " total:" + totalItemCount);
    int lastItemId = view.getLastVisiblePosition();
    //已經(jīng)顯示了最后一條數(shù)據(jù)
    if(lastItemId == pageAddCount -1){
        if(totalLoadedItem >= maxDataSize){
            Toast.makeText(this, "沒有更多的數(shù)據(jù)了", Toast.LENGTH_SHORT).show();
        }
        //數(shù)據(jù)還沒有完全加載完,并且不處于load狀態(tài)
        else if(!isLoading){
            mFooterView.setVisibility(View.VISIBLE);
            new Thread(new Runnable(){

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    isLoading = true;
                    final ArrayList<ItemInfo> newData = getNewData(totalLoadedItem-1);
                    //這里采用延時的方式是為了模擬數(shù)據(jù)加載的過程,假設(shè)數(shù)據(jù)加載用了3s
                    mHandler.postDelayed(new Runnable(){

                        @Override
                        public void run() {
                            mData.clear();
                            mData.addAll(newData);
                            mAdapter.notifyDataSetChanged();
                            mListView.setSelection(0);
                            if(mListView.getFooterViewsCount() != 0)
                                mFooterView.setVisibility(View.GONE);
                            isLoading = false;
                        }
                        
                    }, 3000);
                }
                
            }).start();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容