前言
在開發Android應用過程中經常要與列表展示打交道,比如Listview。在使用過程中如果不能正確的進行細節處理那么對性能還是有很大的損耗的。
Listview展示內容是通過一個Adapter來進行內容綁定的。如下所示:
1 class Adapter implements ListAdapter{ 2 3 @Override 4 public void registerDataSetObserver(DataSetObserver observer) { 5 6 } 7 8 @Override 9 public void unregisterDataSetObserver(DataSetObserver observer) { 10 11 } 12 13 @Override 14 public int getCount() { 15 return items.size(); 16 } 17 18 @Override 19 public Object getItem(int position) { 20 return items.get(position); 21 } 22 23 @Override 24 public long getItemId(int position) { 25 return position; 26 } 27 28 @Override 29 public boolean hasStableIds() { 30 return false; 31 } 32 33 @Override 34 public View getView(int position, View convertView, ViewGroup parent) { 35 ViewHolder vh =null; 36 if(convertView==null){ 37 vh = new ViewHolder(); 38 convertView = mInflater.inflate(R.layout.item, null); 39 vh.tv = (TextView) convertView.findViewById(R.id.tvShow); 40 41 convertView.setTag(vh); 42 }else{ 43 vh = (ViewHolder) convertView.getTag(); 44 } 45 46 vh.tv.setText(items.get(position)); 47 48 return convertView; 49 } 50 51 @Override 52 public int getItemViewType(int position) { 53 return 1; 54 } 55 56 @Override 57 public int getViewTypeCount() { 58 return 1; 59 } 60 61 @Override 62 public boolean isEmpty() { 63 return false; 64 } 65 66 @Override 67 public boolean areAllItemsEnabled() { 68 return false; 69 } 70 71 @Override 72 public boolean isEnabled(int position) { 73 return true; 74 } 75 76 }
有了這個Adapter就可以與Listview進行數據綁定了,如下所示:
lv.setAdapter(new Adapter());
getViw
在Adapter類中有一個重要的實現方法getView用來實現大部分的邏輯,這個就是這篇文章的重點。
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder vh =null; if(convertView==null){ vh = new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null); vh.tv = (TextView) convertView.findViewById(R.id.tvShow); convertView.setTag(vh); }else{ vh = (ViewHolder) convertView.getTag(); } vh.tv.setText(items.get(position)); return 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.
意思是說:獲取一個用來展示數據集中指定位置的數據的視圖。可以通過代碼或者inflate一個XML文件來獲得這個View對象。
Parameters(參數)
position
The position of the item within the adapter's data set of the item whose view we want.
我們要展示的數據集中的數據條目的位置
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 getViewTypeCount()and getItemViewType(int)).
一個能用就用的舊視圖。注意:在使用之前你需要去檢查這個視圖是否非空以及其類型。
如果這個視圖不能用來正確展示數據,那么此方法就需要創建一個新的視圖了。
如果一個列表中有多種視圖類型,那么也可以通過getViewTypeCount\getItemViewType方法來正確使用。
parent
The parent that this view will eventually be attached to
這個視圖要被添加到的對象
convertView
上一段提到了convertView的reuse(重利用)的內容,也就是說在創建一個新視圖去返回給List 的時候需要先檢查下舊的視圖對象是否還可以利用,包括去檢查是否為空以及類型是否正確等等。當然大部分情況下是可以利用的,因此通過這種方式就降低了每次去創建一個item 的view的性能開銷了。
view能夠重復利用了,那么view中的對象是不是也能夠重利用呢,比如其中的展示文本。當然,這就是這篇文章的標題提到的ViewHolder。
ViewHolder
從字面上理解ViewHolder,a holder of the view,就是一個視圖的持有者,持有的內容就是視圖的所有指定內容,或者說是綁定內容(下面會提到綁定)。個人理解這個持有的意思,就好比是一個引用或者一個指針,ViewHolder中的內容發生了變化那么對應的view中也會隨之發生變化;view中的內容發生了變化那么ViewHolder中也隨之發生了變化。這就是所謂的hold吧。下面就看下如果綁定一個ViewHolder。
先要定義一個ViewHolder對象,對象里面的內容就是需要hold的視圖的內容,比如List中每一個item就是一個TextView用來展示信息,那么如下所示:
static final class ViewHolder{ TextView tv; }
Item對象的XML,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="20dip" > <TextView android:layout_marginTop="5dip" android:layout_marginBottom="5dip" android:id="@+id/tvShow" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
ViewHolder中的tv就是用來hold Item找那個的tvShow的。
ViewHolder有了,那么下面就進入綁定過程。還是回過頭來看下getView那個方法片段。
vh = new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null); vh.tv = (TextView) convertView.findViewById(R.id.tvShow); convertView.setTag(vh);
先把ViewHolder和convertView兩個對象建出來,然后通過vh.tv = (TextView) convertView.findViewById(R.id.tvShow);進行了綁定,這樣他們之間就產生了關聯。
最后通過convertView.setTag(vh);方法使得View和ViewHolder產生了關聯,也就是vh真正成為了convertView這個View的holder。
通過這樣的方式,以后如果重利用這個視圖,就可以通過vh = (ViewHolder) convertView.getTag();的方法來吧這個Holder拿出來,修改其中的內容就可以通過下面的方式了vh.tv.setText(items.get(position));
這樣的話省去了findViwyById這樣的查找,降低了開銷
后記:
Listview有很多中優化性能的方式,這個算是其中一種。
完整代碼:ViewHolderDemo
原文連接:http://www.cnblogs.com/luoaz/p/3734999.html