優化使用BaseAdapter

  • 2020 年 2 月 18 日
  • 筆記

個人的上一篇博客,在本文中略有涉及。

本節引言:

上一節中我們學習了如何來使用一個ListView以及自定義一個簡單的BaseAdapter,我們從代碼中可以看出比較重要的兩個方法:getCount()和getView(),界面上有多少列就會調用多少次getView, 這個時候可能看出一些端倪,每次都是新inflate一個View,都要進行這個XML的解析,這樣會 很浪費資源,當然,幾十列或者幾百列的列表並不能體現什麼問題,但假如更多或者布局更加複雜? 所以學習ListView的優化很重要,而本節針對的是BaseAdapter的優化,優化的兩點有,復用convertView 以及使用ViewHolder重用組件,不用每次都findViewById,我們具體通過代碼來體會吧!


1.復用ConvertView:

上面也說了,界面上有多少個Item,那麼getView方法就會被調用多少次! 我們來看看上一節我們寫的getView()部分的代碼:

@Override  public View getView(int position, View convertView, ViewGroup parent) {  //下面這個賦值語句和行數線性相關,資源太浪費了!      convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);      ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);      TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);      TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);        img_icon.setBackgroundResource(mData.get(position).getaIcon());      txt_aName.setText(mData.get(position).getaName());      txt_aSpeak.setText(mData.get(position).getaSpeak());      return convertView;  }

是吧,inflate()每次都要加載一次xml,其實這個convertView是系統提供給我們的可供服用的View 的緩存對象,那就坐下判斷咯,修改下,優化後的代碼:

@Override  public View getView(int position, View convertView, ViewGroup parent) {        if(convertView == null){          convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);      }        ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);      TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);      TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);        img_icon.setBackgroundResource(mData.get(position).getaIcon());      txt_aName.setText(mData.get(position).getaName());      txt_aSpeak.setText(mData.get(position).getaSpeak());      return convertView;  }

 convertView能夠被複用的的最根本原因是:ListView的每一行的View對象結構都是類似的,就拿我的前一篇關於ListView的文章為例,每一行布局的結構都是如下圖所示:

每一次調用如果都通過:

convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);

 新建一個結構完全相同的控件,這是浪費的,所以在第一行布局完成後,我們還是可以利用這個布局對象,只是需要對其重新賦值,比如說改為圖片2;改為str_a1改為str_a2;str_b1改為str_b2,如下圖所示:

2.ViewHolder重用組件

嘿嘿,getView()會被調用多次,那麼findViewById不一樣得調用多次,而我們的ListView的Item 一般都是一樣的布局,我們可以對這裡在優化下,我們可以自己定義一個ViewHolder類來對這一部分進行性能優化!

這裡還是這樣的原因,以下三個語句對象的構造還是和1.中有相同的原理,隨着行數的增多線性增多調用的次數:

ImageView img_icon = convertView.findViewById(R.id.img_icon);    TextView text_aName = convertView.findViewById(R.id.txt_aName);    TextView text_aSpeak = convertView.findViewById(R.id.txt_aSpeak);

 所以我們需要通過類似的判斷,如果img_icon為空,那麼調用findViewById()方法返回此對象引用,否則跳過此步驟,直接調用相關set方法進行資源的覆蓋賦值即可。那麼對於text_name以及text_aSpeak對象也是同理,但是這麼多的判斷語句寫在一起實在不方便,過多的判斷語句效率也不見得高,所以就將這些判斷使用一個ViewHolder對象集成起來。只要判斷ViewHolder對象是否為空即可。

修改後的代碼如下:

@Override  public View getView(int position, View convertView, ViewGroup parent) {      ViewHolder holder = null;      if(convertView == null){          convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);          holder = new ViewHolder();          holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);          holder.txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);          holder.txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);          convertView.setTag(holder);   //將Holder存儲到convertView中      }else{          holder = (ViewHolder) convertView.getTag();      }      holder.img_icon.setBackgroundResource(mData.get(position).getaIcon());      holder.txt_aName.setText(mData.get(position).getaName());      holder.txt_aSpeak.setText(mData.get(position).getaSpeak());      return convertView;  }    static class ViewHolder{      ImageView img_icon;      TextView txt_aName;      TextView txt_aSpeak;  }

 實際沒有 ViewHolder 對象,更沒有判斷ViewHolder是否為空的語句,因為從執行順序上來說,只要有一行初始化過了 convertView 對象,那麼一定就會有初始化img_icon 、txt_aName 、txt_aSpeak 這三個對象,所以我們只需要判斷 convertView 對象是否為空即可。而為何將ViewHolder設置為靜態類的原因可以用如下B神的話來描述:

沒錯就是這麼簡單,你以後BaseAdapter照着這個模板寫就對了,哈哈,另外這個修飾ViewHolder的 static,關於是否定義成靜態,跟裏面的對象數目是沒有關係的,加靜態是為了在多個地方使用這個 Holder的時候,類只需加載一次,如果只是使用了一次,加不加也沒所謂!——Berial(B神)原話~


本節小結:

好的,關於BaseAdapter的優化大概就上述的兩種,非常簡單,復用ConvertView以及自定義ViewHolder 減少findViewById()的調用如果你有其他關於BaseAdapter優化的建議歡迎提出,謝謝