一个小小的新手问题。我们为什么要初始化ViewHolder
中的getView()
?为什么我们不能在构造函数中初始化它?
答案 0 :(得分:29)
您将拥有多个ViewHolder
个对象。
ListView
本质上不会为其每一行创建新的View
个实例。这样,如果您拥有一百万个ListView
个内容,则无需存储一百万个内容的布局信息。那么你需要存储什么?只是屏幕上的东西。然后,您可以反复重复使用这些视图。这样,您的ListView
个百万个对象可能只有10个子视图。
在自定义数组适配器中,您将拥有一个名为getView()
的函数,如下所示:
public View getView(int position, View convertView, ViewGroup parent) {
//Here, position is the index in the list, the convertView is the view to be
//recycled (or created), and parent is the ListView itself.
//Grab the convertView as our row of the ListView
View row = convertView;
//If the row is null, it means that we aren't recycling anything - so we have
//to inflate the layout ourselves.
if(row == null) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.list_item, parent, false);
}
//Now either row is the recycled view, or one that we've inflated. All that's left
//to do is set the data of the row. In this case, assume that the row is just a
//simple TextView
TextView textView = (TextView) row.findViewById(R.id.listItemTextView);
//Grab the item to be rendered. In this case, I'm just using a string, but
//you will use your underlying object type.
final String item = getItem(position);
textView.setText(item);
//and return the row
return row;
}
这会有效,但请花点时间看看你是否能发现这里的低效率。想想上面哪些代码将被冗余调用。
问题在于我们一遍又一遍地呼叫row.findViewById
,即使在我们第一次查看它之后,它也永远不会改变。如果你的列表中只有一个简单的TextView
,那可能不是那么糟糕,如果你有一个复杂的布局,或者你有多个想要设置数据的视图,你可能会失去一点时间查找你的观点一遍又一遍。
那么我们该如何解决这个问题呢?好吧,在我们查找之后将TextView存储在某个地方是有意义的。因此,我们引入了一个名为ViewHolder
的类,它“保存”视图。所以在适配器内部引入一个内部类:
private static class ViewHolder {
TextView textView;
}
这个类是私有的,因为它只是适配器的缓存机制,它是静态的,因此我们不需要引用适配器来使用它。
这将存储我们的视图,这样我们就不必多次调用row.findViewById
。我们应该在哪里设置它?当我们第一次膨胀视图时。我们在哪里存放?视图有一个自定义的“标记”字段,可用于存储有关视图的元信息 - 正是我们想要的!然后,如果我们已经看过这个视图,我们只需要查找标记而不是查找行中的每个视图。
因此getView()
内的if语句变为:
//If the row is null, it means that we aren't recycling anything - so we have
//to inflate the layout ourselves.
ViewHolder holder = null;
if(row == null) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.list_item, parent, false);
//Now create the ViewHolder
holder = new ViewHolder();
//and set its textView field to the proper value
holder.textView = (TextView) row.findViewById(R.id.listItemTextView);
//and store it as the 'tag' of our view
row.setTag(holder);
} else {
//We've already seen this one before!
holder = (ViewHolder) row.getTag();
}
现在,我们只需更新holder.textView的文本值,因为它已经是对回收视图的引用!所以我们的最终适配器代码变为:
public View getView(int position, View convertView, ViewGroup parent) {
//Here, position is the index in the list, the convertView is the view to be
//recycled (or created), and parent is the ListView itself.
//Grab the convertView as our row of the ListView
View row = convertView;
//If the row is null, it means that we aren't recycling anything - so we have
//to inflate the layout ourselves.
ViewHolder holder = null;
if(row == null) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.list_item, parent, false);
//Now create the ViewHolder
holder = new ViewHolder();
//and set its textView field to the proper value
holder.textView = (TextView) row.findViewById(R.id.listItemTextView);
//and store it as the 'tag' of our view
row.setTag(holder);
} else {
//We've already seen this one before!
holder = (ViewHolder) row.getTag();
}
//Grab the item to be rendered. In this case, I'm just using a string, but
//you will use your underlying object type.
final String item = getItem(position);
//And update the ViewHolder for this View's text to the correct text.
holder.textView.setText(item);
//and return the row
return row;
}
我们已经完成了!
要考虑的一些事情:
TextView
个对象和一个ImageView
holder.textView.getText()
getView()
的价值是多少?
答案 1 :(得分:3)
当我们每次填充行时滚动列表并为每一行创建新的行视图时,我们需要初始化视图持有者。就像我在行中有两个TextView一样,
static class ViewHolder {
protected TextView title;
protected TextView type;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
view = inflator.inflate(R.layout.feeds_rowview, null);
final ViewHolder viewHolder = new ViewHolder();
view.setTag(viewHolder);
viewHolder.title = (TextView) view.findViewById(R.id.Title);
viewHolder.type = (TextView) view.findViewById(R.id.Type);
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
holder.title.setText(list.get(position).getTitle());
holder.type.setText(list.get(position).getType());
return view;
}