Android-listvie-優化-convertview/viewholder

http://blog.csdn.net/u010940300/article/details/44196671


使用convertView,viewHolder來優化Listview都是針對Adapter中的getView()方法來優化的

下面這個是沒有優化的getView()方法

 public class FruitAdapter extends ArrayAdapter<Fruit> {

 private int resourceId;

 public FruitAdapter(Context context, int textViewResourceId,List<Fruit> objects) 
 {
     super(context, textViewResourceId, objects);
     resourceId = textViewResourceId;
 }

@Override
public View getView(int position, View convertView, ViewGroup parent) 
{ 
  Fruit fruit = getItem(position); // 獲取當前項的Fruit實例
  View view=LayoutInflater.from(getContext()).inflate(resourceId, null);
  ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
  TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); 
  fruitImage.setImageResource(fruit.getImageId());    
  fruitName.setText(fruit.getName());
  return view;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

這是listview的每個Item的結構

優化點1: 
這個Item顯示的View。如果當Item的數量足夠大,再爲每一個Item都創建一個View對象,必將佔用很多內存。 
(沒有複用,導致每次使用都要創建) 
優化點2: 
創建View對象View view=LayoutInflater.from(getContext()).inflate(resourceId, null); 
從xml中生成View,這是屬於IO操作也是耗時操作,所以必將影響性能。 
優化點3: 
加載ListItem中的TextView,ImageView(就本例來說)

convertView可以解決1和2 
ViewHolder可以解決3

這是Recycler內部原理圖

Android提供了一個叫做Recycler(反覆循環器)的構件, 
就是當ListView的Item從上方滾出屏幕視角之外,對應Item的View會被緩 
存到Recycler中,相應的會從下方生成一個Item, 
而此時調用的getView中的convertView參數就是滾出屏幕的Item的View,所以說 
如果能重用這個convertView,就會大大改善性能。

public class FruitAdapter extends ArrayAdapter<Fruit> {

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
   {   
        Fruit fruit = getItem(position);
        View view;
        if (convertView == null) {
           view = LayoutInflater.from(getContext()).inflate(resourceId, null);
         } else {
          view = convertView;
        }
           ImageView fruitImage = (ImageView)   view.findViewById(R.id.fruit_image); 
           TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); 
           fruitImage.setImageResource(fruit.getImageId()); 
           fruitName.setText(fruit.getName());
        return view;
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

使用convertView優化之後 
可以看到,現在我們在 getView()方法中進行了判斷, 
1.如果 convertView 爲空,則使用 LayoutInflater 去加載佈局, 
2.如果不爲空則直接對 convertView 進行重用。 
(已經創建的View對象可以直接複用) 
(對xml文件中的Layout——轉化——>View對象 這個過程進行優化,不用重複加載佈局文件)

這樣就大大提高了 ListView的運行效率,在快速滾動的時候也可以表現出更好的性能。

現在開始使用ViewHolder進行優化

雖然現在已經不會再重複去加載佈局,但是每次在 getView()方法中還是會調用 View 的 findViewById()方法來獲取一次控件的實例。 我們可以藉助一個 ViewHolder 來對這部分性能進行優化,修改 FruitAdapter 中的代碼,如下

public class FruitAdapter extends ArrayAdapter<Fruit> {

      @Override
      public View getView(int position, View convertView, ViewGroup parent) 
     { 
           Fruit fruit = getItem(position);
           View view;
           ViewHolder viewHolder;

           if (convertView == null) 
          {
               view = LayoutInflater.from(getContext()).inflate(resourceId, null);

           //添加ViewHolder
               viewHolder = new ViewHolder();
               viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
               viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
               //將ImageView,TextView控件保存在ViewHolder中

               view.setTag(viewHolder); // 將ViewHolder存儲在View中
                                       //setTag是用來給 view添加附加信息的  
                            //詳見 http://blog.csdn.net/pkxiuluo01/article/details/7380874


           } else {

               view = convertView;
               viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder
           }

           viewHolder.fruitImage.setImageResource(fruit.getImageId());
           viewHolder.fruitName.setText(fruit.getName());
          //在這裏我們將存放在ViewHolder中的ImageView,TextView實例拿出來使用,而不是findviewById重新創建

       return view;

       }


      class ViewHolder { 
         ImageView fruitImage;
         TextView fruitName;
       }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

我們新增了一個內部類 ViewHolder,用於對控件的實例進行緩存。當 convertView 爲空 的時候,創建一個 ViewHolder 對象,並將控件的實例都存放在 ViewHolder 裏,然後調用 View 的 setTag()方法,將 ViewHolder 對象存儲在 View 中。當 convertView 不爲空的時候則調用 View 的 getTag()方法,把 ViewHolder 重新取出。這樣所有控件的實例都緩存在了 ViewHolder 裏,就沒有必要每次都通過 findViewById()方法來獲取控件實例了。

setTag和getTag的用法 
view的setTag和getTag方法其實很簡單,在實際編寫代碼的時候一個view不僅僅是爲了顯示一些字符串、圖片,有時我們還需要他們攜帶一些其他的數據以便我們對該view的識別或者其他操作。於是android 的設計者們就創造了setTag(Object)方法來存放一些數據和view綁定,我們可以理解爲這個是view 的標籤也可以理解爲view 作爲一個容器存放了一些數據。而這些數據我們也可以通過getTag() 方法來取出來。 
到這裏setTag和getTag大家應該已經明白了。再回到上面的話題,我們通過convertview的setTag方法和getTag方法來將我們要顯示的數據來綁定在convertview上。如果convertview 是第一次展示我們就創建新的Holder對象與之綁定,並在最後通過return convertview 返回,去顯示;如果convertview 是回收來的那麼我們就不必創建新的holder對象,只需要把原來的綁定的holder取出加上新的數據就行了。


發佈了6 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章