在ViewHolder模式中,setTag和getTag的工作原理是什么?

13

我有一个简单的代码片段,用于实现自定义列表视图。

我的代码如下:

WeatherAdapter.java :

public class WeatherAdapter extends ArrayAdapter<weather>{

    Context mcontext; 
    int mlayoutResourceId;    
   weather mdata[] = null;
   View row;

    public WeatherAdapter(Context context, int layoutResourceId, weather[] data) {
        super(context, layoutResourceId, data);
        mlayoutResourceId = layoutResourceId;
       mcontext = context;
        mdata = data;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      row = convertView;
        WeatherHolder holder = null;

        if(row == null)
        {
            LayoutInflater inflater = ( (Activity) mcontext).getLayoutInflater();
            row = inflater.inflate(mlayoutResourceId, parent, false);

            holder = new WeatherHolder(row);


            row.setTag(holder);

        }
        else
        {
            holder = (WeatherHolder)row.getTag();
        }

        weather w = mdata[position];
        holder.txtTitle.setText(w.mtitle);
        holder.imgIcon.setImageResource(w.micon);

        return row;
    }

WeatherHolder.java

class WeatherHolder
    {
        ImageView imgIcon;
        TextView txtTitle;


    public WeatherHolder(View v){

          imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
          txtTitle = (TextView)row.findViewById(R.id.txtTitle);

    }
    }
}

我在Stack Overflow和其他网站上看到了很多答案,我理解了listview的回收机制。

我也明白了从viewholder中,我们可以在适配器中持有子视图,而不必多次调用findViewById()。所以,这是为了优化。

但是我只对setTag(holder)getTag()方法感到困惑。从这个问题中,我知道它是为了在多个对象上创建键值对,以便我们可以轻松访问它们。但是,我不明白为什么它们在这里是必需的...因为我们没有多个holder对象...只需要每次更改holder的变量。我们能够在这里编写代码而不使用setTaggetTag吗?

有人能更好地解释一下在这里setTaggetTag的作用吗?


我看了很多答案,但却找不到适合特定情况的正确答案。 - xyz
1
为什么不尝试用你自己的方式去做,看看结果会是什么呢? - panini
我已经尝试过了...我没有在没有尝试的情况下提出问题...如果你知道,那么请给出答案。 - xyz
3个回答

28

tag是一种机制,可以使你的views记住一些东西,它可以是一个object、一个integer、一个string或者你喜欢的任何东西。

所以当你的ListView第一次创建时,你的convertViewnull的。所以你需要创建一个新的convertView并将该row的所有objects的引用放入一个viewHolder中。然后将你的viewHolder保存到该convertView的内存中(setTag)。Android会将你的convertView放入其pool中以进行recycle,并再次将其passes给你。但是它的pool可能没有足够的convertViews,因此它会再次传递一个新的convertView,即null。所以故事会一遍又一遍地重复,直到android的池被填满。在这之后,android从其池中取出一个convertView并将其传递给你。你会发现它不是null,所以你会问它:我第一次给你的对象引用在哪里?(getTag),然后你就可以得到那些引用并做任何你想做的事情。

以下是上述内容更详细的说明:

但它的池可能没有足够的convertViews,因此它再次传递一个新的convertView,即null

当您的listView即将创建时,Android pool为空。因此,对于您的listView的第一项,它会向您发送一个必须显示的convertView。之后,Android将其存储在其pool中,因此其pool现在只包含一个convertView。对于要创建的第二个listView项目,Android无法使用其pool,因为它实际上只有一个元素,并且该元素是您的第一个项目,正在显示。因此,它必须传递另一个convertView。这个过程重复进行,直到Android在其pool中找到一个当前未显示的convertView并将其传递给您。

Android会填充每一行,直到屏幕填满,之后当您滚动列表时,它会使用holder。


谢谢,但我不理解这句话的意思:"但它的池可能没有足够的convertViews,因此它再次传递一个新的convertView,即null" - 那么在什么情况下,它可能没有足够的convertViews呢? - xyz
简而言之,Android必须填充新行的布局,直到布局适合屏幕大小。然后它使用持有者来更改行中的子视图。对吗? - xyz

18

让我们从不同的角度来看待它:

enter image description here

假设 直升机 是 "", 而绳子是 "setTag",下面的汽车是 "WeatherHolder",但直升机的飞行员在汽车内部,他/她使用 "有线遥控器" 来管理和控制直升机。

当您剪断 "setTag" 的 绳子 时,直升机仍然会飞行,但飞行员不能再控制它,因为飞行员掉落在地上,这意味着飞行员现在已经死亡了!(在Java中,当一个对象失去了引用,垃圾收集器将收集该对象并从内存中释放)。

当您在直升机即将起飞时没有将绳子固定或附加到汽车上,这意味着您使用的是 "有线遥控器",您可能会失去对直升机的控制。

希望这有所帮助:).


2

但是,我不明白为什么它们在这里是必需的...因为我们没有多个持有者对象

你的理解是错误的 - 每个视图(也称可见或缓存的ListView条目)都有一个持有者。


你能详细解释一下吗? - xyz
1
每个视图都有一个单独的“WeatherHolder holder”。 - Shivam Verma
@ShivamVerma 对的。但是 holder = new WeatherHolder(row); 仅在第一次膨胀时调用。(当converview为空或未膨胀布局时) - xyz
它被称为每次视图的首次通货膨胀。 - Shivam Verma
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/61647/discussion-between-xyz-and-shivam-verma。 - xyz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接