当您浏览您的 ListView 时,在任何给定时间只会显示少数视图。这意味着您不必为适配器中的每个项目都实例化视图;当视图滚动到屏幕外时,它可以被重复使用或回收。
视图回收和 ViewHolder 模式不一样。ViewHolder 模式只是为了减少view.findViewById(int)
您的调用次数。ViewHolder 模式仅在您利用视图回收时才有效。
在getView(int position, View convertView, ViewGroup parent)
中,convertView
参数要么是null,要么是一个已被回收的视图:它仍然会绑定来自不同列表项的数据。
如果没有 ViewHolder 模式,您仍然可以利用视图回收(即不盲目地实例化视图):
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = // inflate new view
}
ImageView imageView = (ImageView) view.findViewById(R.id.listitem_image);
TextView textView = (TextView) view.findViewById(R.id.listitem_text);
TextView timestampView = (TextView) view.findViewById(R.id.listitem_timestamp);
ProgressBar progressSpinnerView = (ProgressBar) view.findViewById(R.id.progress_spinner);
// TODO: set correct data for this list item
// imageView.setImageDrawable(...)
// textView.setText(...)
// timestampView.setText(...)
// progressSpinnerView.setProgress(...)
return view;
}
上面是一个视图回收的例子——我们不会为每一行膨胀一个新的视图;如果我们没有得到一个可以重用的视图,我们只会夸大一个视图。避免让视图膨胀是在滚动列表时肯定有助于提高性能的部分:利用视图回收。
那么,ViewHolder 有什么用呢?无论行本身是否已经存在,我们目前findViewById(int)
对每个项目都执行 4 倍。当findViewById(int)
递归迭代 ViewGroup 直到找到具有给定 ID 的后代时,这对于我们回收的视图来说有点没有意义——我们正在重新查找我们已经引用过的视图。
通过在“找到”子视图后使用 ViewHolder 对象来保存对子视图的引用来避免这种情况:
private static class ViewHolder {
final TextView text;
final TextView timestamp;
final ImageView icon;
final ProgressBar progress;
ViewHolder(TextView text, TextView timestamp, ImageView icon, ProgressBar progress) {
this.text = text;
this.timestamp = timestamp;
this.icon = icon;
this.progress = progress;
}
}
View.setTag(Object)
允许您告诉 View 持有任意对象。如果我们在findViewById(int)
调用之后使用它来保存 ViewHolder 的实例,那么我们可以View.getTag()
在回收的视图上使用它来避免一次又一次地进行调用。
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = // inflate new view
ViewHolder holder = createViewHolderFrom(view);
view.setTag(holder);
}
ViewHolder holder = view.getTag();
// TODO: set correct data for this list item
// holder.icon.setImageDrawable(...)
// holder.text.setText(...)
// holder.timestamp.setText(...)
// holder.progress.setProgress(...)
return view;
}
private ViewHolder createViewHolderFrom(View view) {
ImageView icon = (ImageView) view.findViewById(R.id.listitem_image);
TextView text = (TextView) view.findViewById(R.id.listitem_text);
TextView timestamp = (TextView) view.findViewById(R.id.listitem_timestamp);
ProgressBar progress = (ProgressBar) view.findViewById(R.id.progress_spinner);
return new ViewHolder(text, timestamp, icon, progress);
}
这种优化的性能优势值得怀疑,但这就是ViewHolder 的优势。