以下文章內容來自網絡復制粘貼,由於是之前整理到筆記本的部分找不到出處。如有侵犯,敬請告知。
【0】ListView中getView的工作原理:
[1]ListView asks adapter “give me a view” (getView) for each item of the list.(通過getView來獲取每個item)
[2]A new View is returned and displayed(獲取到后返回顯示)
那么如果我們有大量的數據需要顯示的時候,每個Item都去重復執行getView中的創建新的View的動作嗎?這樣做會耗費大量的資源去執行重復的事情,實際上Android為我們提供了一套重復利用的機制叫做“Recycler”:
原理簡單描述下就是這樣:
在一個完整的ListView第一次出現時,每個Item都是Null的,getView的時候會跑到需要inflate一個Item的代碼段,假設整個view只能最多顯示10個item,那么當滑動到第11個Item的時候,第一個item會放入“recycler”,如果第11個Item和放入“Recycler”的item的view一致,那么就會使用"Recycler"里面的Item來顯示,從而不用再重復inflate一次,這樣大大節省了創建View的工作,在需要顯示大量數據時顯得尤為重要。
工作原理的示意圖如下:

學習自http://android.amberfog.com/?p=296:
Demo:
這是一個getView的方法,其他細節的Code就不顯示了
- static class ViewHolder
 - {
 - public ImageView localImageView = null;
 - public TextView localTextView1 = null;
 - public TextView localTextView2 = null;
 - public TextView localTextView3 = null;
 - }
 - public View getView(int paramInt, View paramView, ViewGroup paramViewGroup)
 - {
 - logger.i("This is position:" + paramInt);
 - WrapperSonglist localUserShareSonglistEntity = (WrapperSonglist) getItem(paramInt);
 - if(localUserShareSonglistEntity != null)
 - {
 - ViewHolder holder = null;
 - if(paramView == null)
 - {
 - this.logger.d("convertView == null,Then inflate and findViewById");
 - paramView = this.mInflater.inflate(R.layout.listitem04, paramViewGroup, false);
 - holder = new ViewHolder();
 - holder.localImageView = (ImageView) paramView.findViewById(R.id.listitem04ImageView);
 - holder.localTextView1 = (TextView) paramView.findViewById(R.id.listitem04TextView01);
 - holder.localTextView2 = (TextView) paramView.findViewById(R.id.listitem04TextView02);
 - holder.localTextView3 = (TextView) paramView.findViewById(R.id.listitem04TextView03);
 - paramView.setTag(holder);
 - }
 - else
 - {
 - //Used ViewHolder to improve performance
 - this.logger.d("convertView != null,Then findViewById(get Holder)");
 - holder = (ViewHolder) paramView.getTag();
 - }
 - if(paramView != null)
 - {
 - this.logger.d("convertView != null,Then SetValue");
 - String mstr = localUserShareSonglistEntity.getSonglistImage();
 - int id = localUserShareSonglistEntity.getSonglistId();
 - holder.localTextView1.setText("[id]:"+id+",bitmap:url:"+mstr);
 - String name = localUserShareSonglistEntity.getSonglistName();
 - holder.localTextView2.setText("[Name]:"+name);
 - String url = localUserShareSonglistEntity.getSonglistUrl();
 - holder.localTextView3.setText("[Url]:"+url);
 - }
 - }
 - return paramView;
 - }
 
當我們第一次啟動到Listview的時候如下,只顯示了5個Item,那么getView被調用5次:

打印的Log如下:可以看到從0-4都是null,我們需要做inflate的動作,
- 01-12 17:58:22.144: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:0
 - 01-12 17:58:22.154: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
 - 01-12 17:58:22.174: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:1
 - 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
 - 01-12 17:58:22.184: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:2
 - 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
 - 01-12 17:58:22.194: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:3
 - 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
 - 01-12 17:58:22.204: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:4
 - 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 17:58:22.214: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong>
 - </strong>
 
 
當我們小心往下滑動一個位置,剛出現第6個Item,此時第1個還沒有消失時,

這個時候只打印了一個獲取第6個Item的Log,如下:可以看到第6個Item還是為null,需要inflate出來新的
- 01-12 18:02:37.623: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:5
 - 01-12 18:02:37.623: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
 - 01-12 18:02:37.633: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong>
 - </strong>
 
 
如果此時再往下滑動列表,第1個item此時會放入Recycler,第7個Item此時不再是null,而是利用了第1個item的View,如下:

從下面的Log,可以看到從第7個開始就不是null了
01-12 18:52:36.243: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:6
01-12 18:52:36.243: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:36.243: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:36.693: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:7
01-12 18:52:36.693: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:36.693: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:37.024: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:8
01-12 18:52:37.024: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:37.034: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
01-12 18:52:37.604: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:9
01-12 18:52:37.604: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:89 getView ] - convertView != null,Then findViewById
01-12 18:52:37.604: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue
等所有的item,一共10個都顯示之后,不管上下滑動都再也不是NULL了,說明這個時候都是使用Recycle里面的view,而不會再重新inflate了,顯然這樣節省很多重復的操作
【1】重復多輪調用getView的解決方案
當我們在使用listview的時候。有時候自定義adapter的時候,是不是會發現在getview里打印日志的時候,重復調用很多次?有時候4次。有的嚴重甚至到10次,當我們在listview中移動的時候。每移動一列都會調用很多次,這樣大大影響到效率!其實這和listview本身在android上的機制有關。下面我開始來介紹一下吧:
在布局,我們只有一個listview的時候。那好。我們把高設置成wrap_content的時候。在listview里加載幾行看看。日志在getview里打印一下。是不是重復調用了?那這個辦法就好弄了。把高設置成fill_parent就成了。這個時候發現日志還是重復調用?那就要看一下Listview的上一級而已的高是不是也是設置也fill_parent的,如果不是。請改動吧。如果是。。。那我還真沒碰到重復調用的!因為測試好幾次了!
如果我們在而已里不只一個Listview。一個復雜好看的布局可能有很多。listview在布局的某個地方。這個時候有時候運氣不好。你會發現你調用了很多次getview。我測試的時候。最高230次。。。可想而知。這個速度是相當慢。而且每移動一次就是調用這么多次!對於這樣的情況,在修改布局的時候,要考慮以下兩點:1.首先考慮需求布局和性能哪個更重要一點。2.考慮listview周邊哪個布局控件影響到了它!
如果在性能上沒有太大影響,而需求要求必需是那樣的布局。那就以布局為主。看看有沒有別的方法來優化一下listview,當然前提是布局一點都不能調整。如果能調整,布局沒有太大變動。而listview又能很好的優化。那就當然優化了!當我們優化的時候。首先要看一下有沒有影響到Listview重繪的控件,比如。如果它上面和下面都有控件。而且高都是wrap_content,那么你就要設置成fill_parent或者固定高。這樣listview在高上就不會重繪,這是最主要的一點。那左右是不是也有控件(一般一個手機頁面用到list的時候不會有這么多控件)?有,那我們就也要調整,那就同高一樣的設置。一定要讓listview是一個固定在那個地方不動的。不然,你就等着讓他重復去調用吧!
其實說了這么多。最主要的還是在我們進行布局的時候。要巧妙的運用每個控件的屬性,以及了解控件每個的原理。這樣在我們進行UI設計的時候,才能很好的去結合!
