使用视图持有者时,listView 中的行回收如何工作?

How does the recyclying of rows in an listView work when working with a view holder?

我已经为我的 ListView 行定义了一个 ViewHolder,如下所示:

import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
class ViewHolder 
{
ImageView icon=null;
TextView size=null;
ViewHolder(View row) 
{
   this.icon=(ImageView)row.findViewById(R.id.icon);
   this.size=(TextView)row.findViewById(R.id.size);
}
}

和适配器 class 的 getView() 方法如下所示:

@Override
public View getView(int position, View convertView,
ViewGroup parent) {
View row=super.getView(position, convertView, parent);
ViewHolder holder=(ViewHolder)row.getTag();
if (holder==null) {
holder=new ViewHolder(row);
row.setTag(holder);
}
if (getModel(position).length()>4) {
holder.icon.setImageResource(R.drawable.delete);
}
else {
holder.icon.setImageResource(R.drawable.ok);
}
holder.size.setText(String.format(getString(R.string.size_template), items[position].length()));
return(row);
}

现在我的问题是:

  1. Viewreturn上的getTag()调用在什么情况下会为空?

  2. row.setTag(holder) 有什么作用以及它如何重新初始化 ViewHolder?

  3. ListView 中的视图回收是如何工作的?getTag()setTag() 在使用适配器时如何帮助我们解决这个问题?

我对工作机制有点迷惑,搜索了Stack Overflow也没有找到任何结果。

这是一个很好的question.So 为了了解标签在适配器中的行为,我们需要了解 listView 中回收流程的实际行为。

Tag 是一个让我们的 views 记住一些东西的小工具,它可以是一个对象,一个整数或任何其他东西,所以当我们的 ListView 去创建第一个当时 convertViewnull,所以我们创建一个新的 convertView 并将我们对 row objects 的所有引用放在 viewHolder,然后将我们的 viewHolder 保存到那个 convertView(setTag) 的内存中。 Android 获取我们的 convertView 并将其放入其池内存中以回收它并再次传递给我们。但是它的池内存可能没有足够的 convertViews 所以它再次传递一个新的 convertView 那就是 null。所以再次重复这个故事,直到 android 的池被填满。之后 android 从其池中取出一个 convertView 并将其传递给我们。你会发现它不是空的,所以你问它我第一次使用 getTag() 给你的对象引用在哪里,这样你就可以得到它们并做任何你喜欢的事情。

这是list ViewHolder的处理流程,如果你想了解更多关于setTag()和getTag()的知识,我给你举个例子:

假设您正在创建一堆具有类似行为的视图,例如:

button1.setOnClickListener(new OnClickListener ... );
button2.setOnClickListener(new OnClickListener ... );

然后您将重写方法 onClick(),例如:

private void onClick(View view) {
    doAction(0); // here 0 for button 1 and 1 for button 2. 
}

这是因为您在 onclick() 方法中只有一个参数 View,您可以使用 setTag() 来更改其行为,使用值“0”和“1”来访问不同的按钮: 只需添加

button1.setTag(0);
button2.setTag(1);

现在,您可以通过使用函数 getTag() 将标签附加到视图来为每个按钮使用相同的 OnClickListener:

listener = new OnClickListener() {
    @Override
    public void onClick(View view) {
        doAction(view.getTag());
    }
};

这是因为您使用 setTag() 方法将视图放入池内存,然后相应地从池中获取这些视图。