在开发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());
在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的reuse(重利用)的内容,也就是说在创建一个新视图去返回给List 的时候需要先检查下旧的视图对象是否还可以利用,包括去检查是否为空以及类型是否正确等等。当然大部分情况下是可以利用的,因此通过这种方式就降低了每次去创建一个item 的view的性能开销了。
view能够重复利用了,那么view中的对象是不是也能够重利用呢,比如其中的展示文本。当然,这就是这篇文章的标题提到的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