Android優化系列之ListView優化老生常談


 

本文內容:adapter,listview的優化,RecycleBi,google大會推薦優化,

實現ListView的過程,Adapter起到了至關重要的作用,不僅僅因為getview()方法。那么,先從Adapter說起~

Adapter:

    它在ListView和數據源之間起到橋梁的作用,避免listview和數據源直接接觸,而導致因為數據源的復雜性使listview顯得臃腫。

  Adapter,適配器,把復雜的數據源適配給listview,很容易聯想到適配器模式。

 

下面是幾種常用的Adapter:

  •  ArrayAdapter:簡單易用的Adapter,通常用於數組或list集合的數據源(多個值包裝成多個列表項)。
  •  simpleAdapter:並不見得、功能強大的Adapter,可用於list集合的多個對象包裝成多個列表項。
  •  simpleCursorAdapter:與上相似,但是用於包裝jCursor(數據庫游標)提供的數據源。
  •   BaseAdapter:通常用於被擴展。擴展BaseAdapter可以對各列表項進行最大限度的定制。

 


下面是listview的優化:

增加優化一:convertView的使用,主要優化加載布局問題

  1.listivew每次滾動都會調用gitview()方法,所以優化gitview是重中之重。

    下面是getview()在Adapter類的源碼,這個沒有實現,要看重點部分已經顏色標記。無非是View convertView的介紹~

/**
     * Get a View that displays the data at the specified position in the data set. You can either
     * create a View manually or inflate it from an XML layout file. When the View is inflated, the
     * parent View (GridView, ListView...) will apply default layout parameters unless you use
     * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
     * to specify a root view and to prevent attachment to the root.
     * 
     * @param position The position of the item within the adapter's data set of the item whose view
     *        we want.
    * @param convertView The old view to reuse, if possible. Note: You should check that this view
     *        is non-null and of an appropriate type before using. If it is not possible to convert
     *        this view to display the correct data, this method can create a new view.
     *        Heterogeneous lists can specify their number of view types, so that this View is
     *        always of the right type (see {@link #getViewTypeCount()} and
     *        {@link #getItemViewType(int)}).
     * @param parent The parent that this view will eventually be attached to
     * @return A View corresponding to the data at the specified position.
     */
    View getView(int position, View convertView, ViewGroup parent);


大家對於 convertView = null優化方法的使用已經了然於胸,但是我那個糾結,就知道是緩存了listview里面已經加載好的view(下文會講解)。

核心代碼如下:

     這部分代碼很簡單,如果沒有緩存就加載布局,如果有緩存就直接用convertView對象。所以這樣就不用滑動listview的時候

調用getView()方法每次都去加載布局了(如果改布局已經加載)。

View view;
if(convertView == null){
view = LayoutInfalter.from(getContext()).inflate(resourceID,null)
}
else{
view = convertView
}

表示寶寶一開始對 LayoutInfalter.from(getContext()).inflate(resourceID,null) 一臉蒙蔽,然后找到了解釋。

//加載布局管理器
LayoutInflater inflater = LayoutInflater.from(context);
//將xml布局轉換為view對象
convertView = inflater.inflate(R.layout.item_myseallist,parent,  false );
//利用view對象,找到布局中的組件
convertView.findViewById(R.id.delete); // 刪除

 

增加優化二:內部類ViewHolder的使用。

           代碼如下主要優化getView方法中每次回調用findviewByID()方法來獲取一次控件的代碼。

新增加內部類ViewHolder,用於對控件的實力存儲進行緩存。

  • convertView為空時,viewHolder會將空間的實力存放在ViewHolder里,然后用setTag方法講viewHolder對象存儲在view里。
  • convertView不為空時,用getTag方法獲取viewHolder對象.
//getView核心代碼
ViewHolder viewHolder;
if(convertView == null){
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewByID(R.id.fruit_image);
view.setTage(viewHolder);//講ViewHolder存儲在View中

}else{
view = convertView;
viewHolder = ViewHolder view.getTag();//重獲取viewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getIMageID);



//內部類
class ViewHolder{
ImageView fruitImage;
}


可以看到方案一二目的很明確:第一個是優化加載布局,第二個是優化加載控件。

回到我問題~convertView存儲的問題。

有沒有想過ListView加載成千上萬的數據為什么不出OOM錯誤?

最主要的是因為RecycleBin機制。

 

  • listview的許多view呈現在Ui上,這樣的View對我們來說是可見的,可以稱為OnScreen的view(也為ActionView)。
  • view被上滾移除屏幕,這樣的view稱為offScreenView(也稱為ScrapView)。
  • 然后ScrapView會被listview刪除,而RecycleView會將這部分保存。
  • 而listview底部需要顯示的view會從RecycleBin里面取出一個ScrapView。

將其作為convertView參數傳遞過去,

  • 從而達到View復用的目的,這樣就不必在Adapter的getView方法中執行LayoutInflater.inflate()方法了(不用加載布局了有木有)。

 在RecycleBin里面有兩個數組,看名字就知道了Actionview和ScrapViews.

/**
         * Views that were on screen at the start of layout. This array is populated at the start of
         * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.
         * Views in mActiveViews represent a contiguous range of Views, with position of the first
         * view store in mFirstActivePosition.
         */
        private View[] mActiveViews = new View[0];

        /**
         * Unsorted views that can be used by the adapter as a convert view.
         */
        private ArrayList<View>[] mScrapViews;

 原理如下:

Google推薦優化方案: 

public View getView(int position, View convertView, ViewGroup parent) {
   3:     Log.d("MyAdapter", "Position:" + position + "---"
   4:             + String.valueOf(System.currentTimeMillis()));
   5:     ViewHolder holder;
   6:     if (convertView == null) {
   7:         final LayoutInflater inflater = (LayoutInflater) mContext
   8:                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   9:         convertView = inflater.inflate(R.layout.list_item_icon_text, null);
  10:         holder = new ViewHolder();
  11:         holder.icon = (ImageView) convertView.findViewById(R.id.icon);
  12:         holder.text = (TextView) convertView.findViewById(R.id.text);
  13:         convertView.setTag(holder);
  14:     } else {
  15:         holder = (ViewHolder) convertView.getTag();
  16:     }
  17:     holder.icon.setImageResource(R.drawable.icon);
  18:     holder.text.setText(mData[position]);
  19:     return convertView;
  20: }
  21:  
  22: static class ViewHolder {
  23:     ImageView icon;
  24:  
  25:     TextView text;

 

    推薦幾個鏈接:listview源碼理解:很長,我是沒看完~http://www.bkjia.com/Androidjc/1037874.html。取其中一張圖,源碼不是最新版本。

           RecycleBin機制:http://www.2cto.com/kf/201604/497754.html。解決了我的疑問。

                                列舉下真正意義上的優化:http://www.xuanyusong.com/archives/1252。                     

致敬前輩。如果有錯誤請提出。

本文出處:http://www.cnblogs.com/yuhanghzsd/p/5595532.html       

      2016-06-18       

           

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM