View的setTag()getTag()方法的主要目的是什么?


Answers:


636

假设您生成了一堆相似的视图。您可以分别OnClickListener为每个视图设置一个:

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

然后,onClick即使它们执行类似的操作,也必须为每个视图创建唯一的方法,例如:

public void onClick(View v) {
    doAction(1); // 1 for button1, 2 for button2, etc.
}

这是因为onClick只有一个参数a View,并且它必须从封闭范围中的实例变量或最终局部变量获取其他信息。我们真正想要的是从视图本身获取信息。

输入getTag/ setTag

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

现在,我们可以对每个按钮使用相同的OnClickListener:

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

基本上,这是一种让视图具有记忆力的方法


8
@Matthew Willis,但是我们也可以使用view.getId()来做到这一点。是不是 ?
Android Killer 2012年

50
您可以使用@AndroidKiller,但可以使用setTag()放置所需的任何对象,甚至包括自定义类-这样您就可以使用它们来持久保存对视图显示的数据的引用
Daniel

如果我只想更改所单击按钮的背景颜色,该怎么办?我正在通过getTag()获得职位。
萨加

2
@Sagar:public void ui_click(View view){ if(20==((int)view.getTag())) view.setBackgroundColor(colorInt); }应对颜色部分有用。20只是用于验证View位置的占位符。
RiA

我认为这是旧方法。新方法是使用提供类型安全性的通用参数。但这还是很好。
M.kazem Akhgary '18

124

我想补充几句话。

尽管get/setTag(Object)在ViewHolder模式的特定情况下使用似乎很有用,但我建议在其他情况下使用之前再三考虑。几乎总是有另一个具有更好设计的解决方案。

主要原因是这样的代码很快变得不被支持。

  • 对于其他开发人员而言,您打算将其存储为标签的内容并不明显。这些方法setTag/getTag根本不是描述性的。

  • 它只存储一个Object,需要时强制将其强制转换getTag。当您决定更改标记中存储的对象的类型时,以后可能会遇到意外崩溃。

  • 这是一个真实的故事:我们有一个很大的项目,其中包含许多适配器,带有视图的异步操作等。一个开发人员决定set/getTag在他的代码部分中这样做,但是另一位开发人员已经将标签设置为此视图。最后,某人找不到自己的标签,感到非常困惑。我们花了几个小时才能找到该错误。

setTag(int key, Object tag)看起来好多了,因为您可以为每个标签生成唯一的密钥(使用id资源),但是对于Android <4.0,存在明显的限制。从Lint docs:

在Android 4.0之前,View.setTag(int,Object)的实现会将对象存储在静态映射中,在其中强烈引用了值。这意味着,如果对象包含指向上下文的任何引用,则上下文(指向几乎所有其他内容)将泄漏。如果传递视图,则该视图将提供对创建该视图的上下文的引用。同样,视图持有者通常包含一个视图,并且光标有时也与视图相关联。


2
谢谢,非常有帮助!...您是否偶然知道活动娱乐之间是否恢复了标签中的内容?
贡纳尔

25

我们可以根据需要使用setTag()getTag()来设置和获取自定义对象。该setTag()方法接受type参数Object,并getTag()返回Object

例如,

Person p = new Person();
p.setName("Ramkailash");
p.setId(2000001);
button1.setTag(p);

20

对于Web开发人员,这似乎等效于data ..


14

这对于自定义ArrayAdapter使用非常有用。这是某种优化。有setTag用作参考对象上布局的某些部分的引用(即在显示ListView)代替findViewById

static class ViewHolder {
    TextView tvPost;
    TextView tvDate;
    ImageView thumb;
}

public View getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        LayoutInflater inflater = myContext.getLayoutInflater();
        convertView = inflater.inflate(R.layout.postitem, null);

        ViewHolder vh = new ViewHolder();
        vh.tvPost = (TextView)convertView.findViewById(R.id.postTitleLabel);
        vh.tvDate = (TextView)convertView.findViewById(R.id.postDateLabel);
        vh.thumb = (ImageView)convertView.findViewById(R.id.postThumb);
        convertView.setTag(vh);
    }
            ....................
}


11

当您拥有ListView并想要回收/重用视图时,设置TAG确实很有用。这样,ListView就变得与更新的RecyclerView非常相似。

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

if ( convertView == null )
{
    /* There is no view at this position, we create a new one. 
       In this case by inflating an xml layout */
    convertView = mInflater.inflate(R.layout.listview_item, null);  
    holder = new ViewHolder();
    holder.toggleOk = (ToggleButton) convertView.findViewById( R.id.togOk );
    convertView.setTag (holder);
}
else
{
    /* We recycle a View that already exists */
    holder = (ViewHolder) convertView.getTag ();
}

// Once we have a reference to the View we are returning, we set its values.

// Here is where you should set the ToggleButton value for this item!!!

holder.toggleOk.setChecked( mToggles.get( position ) );

return convertView;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.