Android中的適配器(Adapter)是數據與視圖(View)之間的橋樑,用於對要顯示的數據進行處理,並經過綁定到組件進行數據的顯示。android
BaseAdapter是Android應用程序中常常用到的基礎數據適配器的基類,它實現了Adapter接口。其主要用途是將一組數據傳到像ListView、Spinner、Gallery及GridView等UI顯示組件進行顯示。咱們常用的ListView 的adapter(即SimpleAdapter),是繼承自BaseAdapter基類的。BaseAdapter是一個基類,沒有實現綁定數據的功能。而SimpleAdapter實現了基本控件的綁定,如TextView,Button,ImageView等。並已經爲咱們實現好了數據優化工做。算法
這些適配器使用相同組件動態綁定數據的方式進行優化。爲何須要優化呢?由於若是咱們有上億個(較多個)項目要顯示怎麼辦?爲每一個項目建立一個新視圖?這不可能,由於內存有限制。實際上Android爲你緩存了視圖。Android中有個叫作Recycler的構件,下圖是他的工做原理:緩存
若是你有10億個項目(item),其中只有可見的項目存在內存中,其餘的在Recycler中。其實個人理解Recyler就是一個隊列,用來存儲不在屏幕範圍內的item,若是item滾出屏幕範圍,那麼就入隊,這裏的滾出是徹底滾出,即邊界等也要徹底滾出。若是新的item要滾進來,那麼android系統的framework就會查看Recyler是否含有能夠重複使用的View,若是有那麼就從新設置該View 的數據源,而後顯示,即出隊。那麼這麼多的item其實只須要佔用必定空間的內存,這個內存大小是多少呢?個人感受是手機屏幕所包含的item的個數,再加上1,而後乘以每一個item佔用的內存。可是最後我發現是加上2.多是爲了使得緩存更大吧。。。。可是爲何加上2,你們應該理解,若是你不理解,那你就把滾動list的過程好好想想。那個隊列無非就是一個緩存罷了,由於咱們的目的是經過那個緩存來重複使用那些已經建立的View。數據結構
使用BaseAdapter必須寫一個類繼承它,同時BaseAdapter是一個抽象類,繼承它必須實現它的方法。BaseAdapter的靈活性就在於它要重寫不少方法,看一下有哪些方法,如圖4-35所示爲繼承自BaseAdapter的SpeechListAdapter所實現的方法,其中最重要的即爲getView()方法。這些方法都有什麼做用呢?咱們經過分析ListView的原理來爲讀者解答。 ide
當系統開始繪製ListView的時候,首先調用getCount()方法。獲得它的返回值,即ListView的長度。而後系統調用getView()方法,根據這個長度逐一繪製ListView的每一行。也就是說,若是讓getCount()返回1,那麼只顯示一行。而getItem()和getItemId()則在須要處理和取得Adapter中的數據時調用。那麼getView如何使用呢?若是有10000行數據,就繪製10000次?這確定會極大的消耗資源,致使ListView滑動很是的慢,那應該怎麼作呢?經過一個例子來說解如何在使用BaseAdapter的時候優化ListView的顯示。例子中將上一節中的ImageView換成Button,而且處理Button的點擊事件,其中對ListView的顯示作了優化。佈局
在BaseAdapter中須要實現一個繼承自BaseAdapter的類,而且重寫裏面的不少方法,例如性能
1 class MyAdapter extends BaseAdapter 2 { 3 private Context context; 4 public MyAdapter(Context context) 5 { 6 this.context = context; 7 } 8 @Override 9 public int getCount() { 10 // How many items are in the data set represented by this Adapter.(在此適配器中所表明的數據集中的條目數) 11 return 0; 12 } 13 14 @Override 15 public Object getItem(int position) { 16 // Get the data item associated with the specified position in the data set.(獲取數據集中與指定索引對應的數據項) 17 return null; 18 } 19 20 @Override 21 public long getItemId(int position) { 22 // Get the row id associated with the specified position in the list.(取在列表中與指定索引對應的行id) 23 return 0; 24 } 25 26 @Override//用來刷新它所在的ListView的.在每一次item從屏幕外滑進屏幕內的時候,或者程序剛開始的時候建立第一屏item的時候調用 27 public View getView(int position, View convertView, ViewGroup parent) { 28 // Get a View that displays the data at the specified position in the data set. 29 return null; 30 } 31 32 }
這裏面沒什麼難度,可是這個getView方法必須好好處理,也是最麻煩的優化
第一種:沒有任何處理,不建議這樣寫。若是數據量少看將就,可是若是列表項數據量很大的時候,會每次都從新建立View,設置資源,嚴重影響性能,因此從一開始就不要用這種方式this
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 View item = mInflater.inflate(R.layout.list_item, null); 4 ImageView img = (ImageView)item.findViewById(R.id.img) 5 TextView title = (TextView)item.findViewById(R.id.title); 6 TextView info = (TextView)item.findViewById(R.id.info); 7 img.setImageResource(R.drawable.ic_launcher); 8 title.setText("Hello"); 9 info.setText("world"); 10 11 return item; 12 }
第二種ListView優化:經過緩存convertView,這種利用緩存contentView的方式能夠判斷若是緩存中不存在View才建立View,若是已經存在能夠利用緩存中的View,提高了性能spa
1 public View getView(int position, View convertView, ViewGroup parent) { 2 if(convertView == null) 3 { 4 convertView = mInflater.inflate(R.layout.list_item, null); 5 } 6 7 ImageView img = (ImageView)convertView.findViewById(R.id.img) 8 TextView title = (TextView)convertView.findViewById(R.id.title); 9 TextView info = (TextView)ConvertView.findViewById(R.id.info); 10 img.setImageResource(R.drawable.ic_launcher); 11 title.setText("Hello"); 12 info.setText("world"); 13 14 return convertView; 15 }
第三種ListView優化:經過convertView+ViewHolder來實現,ViewHolder就是一個靜態類,使用 ViewHolder 的關鍵好處是緩存了顯示數據的視圖(View),加快了 UI 的響應速度。
當咱們判斷 convertView == null 的時候,若是爲空,就會根據設計好的List的Item佈局(XML),來爲convertView賦值,並生成一個viewHolder來綁定converView裏面的各個View控件(XML佈局裏面的那些控件)。再用convertView的setTag將viewHolder設置到Tag中,以便系統第二次繪製ListView時從Tag中取出。(看下面代碼中)
若是convertView不爲空的時候,就會直接用convertView的getTag(),來得到一個ViewHolder。
1 //在外面先定義,ViewHolder靜態類 2 static class ViewHolder 3 { 4 public ImageView img; 5 public TextView title; 6 public TextView info; 7 } 8 //而後重寫getView 9 @Override 10 public View getView(int position, View convertView, ViewGroup parent) { 11 ViewHolder holder; 12 if(convertView == null) 13 { 14 holder = new ViewHolder(); 15 convertView = mInflater.inflate(R.layout.list_item, null); 16 holder.img = (ImageView)item.findViewById(R.id.img) 17 holder.title = (TextView)item.findViewById(R.id.title); 18 holder.info = (TextView)item.findViewById(R.id.info); 19 convertView.setTag(holder); 20 }else 21 { 22 holder = (ViewHolder)convertView.getTag(); 23 holder.img.setImageResource(R.drawable.ic_launcher); 24 holder.title.setText("Hello"); 25 holder.info.setText("World"); 26 } 27 28 return convertView; 29 }
到這裏,可能會有人問ViewHolder靜態類結合緩存convertView與直接使用convertView有什麼區別嗎,是否重複了
在這裏,官方給出瞭解釋
提高Adapter的兩種方法
To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary
(譯:重用緩存convertView傳遞給getView()方法來避免填充沒必要要的視圖)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary
(譯:使用ViewHolder模式來避免沒有必要的調用findViewById():由於太多的findViewById也會影響性能)
ViewHolder類的做用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked
(譯:ViewHolder模式經過getView()方法返回的視圖的標籤(Tag)中存儲一個數據結構,這個數據結構包含了指向咱們
要綁定數據的視圖的引用,從而避免每次調用getView()的時候調用findViewById())