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

458
这些方法(setTag()getTag())的主要目的是什么?它们是用来处理 View 类型对象的。
我是否可以将多个对象与单个 View 关联起来?
7个回答

670

假设您生成了一堆类似的视图。 您可以为每个视图单独设置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只有一个参数,即View,它必须从实例变量或封闭作用域中的final局部变量获取其他信息。我们真正想要的是从视图本身获取信息。

使用getTag/setTag

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

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

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

这基本上是为视图提供记忆的一种方式。


10
但我们也可以使用view.getId()来实现这个功能,是吗? - Android Killer
60
@AndroidKiller,你可以使用setTag()方法,但它允许您放置任何对象,甚至是自定义类 - 因此,您可以使用它们来持久化对该视图显示的数据的引用。 - Daniel
如果我只想更改所单击的按钮的背景颜色,该怎么办?我正在通过getTag()获取位置。 - Sagar Devanga
3
@Sagar: 对于颜色部分,public void ui_click(View view){ if(20==((int)view.getTag())) view.setBackgroundColor(colorInt); } 应该能起作用。其中的20只是您的视图验证位置的占位符。 - Glitch
我认为这是旧的方法。新的方法是使用泛型参数,提供类型安全性。但这仍然不错。 - M.kazem Akhgary

142

我想补充几句话。

虽然在ViewHolder模式的特定情况下使用get/setTag(Object)似乎非常有用,但我建议在其他情况下使用之前三思而后行。几乎总会有更好的设计方案。

主要原因是这种代码很快就无法支持。

  • 对于其他开发人员来说,你设计的标签存储内容不容易理解。setTag/getTag 方法根本没有描述性。

  • 它只是存储一个对象,当你想要getTag时需要进行类型转换。当你决定改变标签中存储对象的类型时,可能会导致意外崩溃。

  • 以下是一个真实的故事:我们有一个非常大的项目,其中有很多适配器、异步操作和视图等等。一名开发者决定在他的代码中使用set/getTag,但另一个开发者已经为该视图设置了标签。最终,有人找不到自己的标签,非常困惑。这花费了我们数小时来查找错误。

setTag(int key, Object tag)看起来更好,因为你可以为每个标签生成唯一的键(使用id资源),但在Android < 4.0中有一个重要限制。来自Lint文档:

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


2
谢谢,非常有帮助!你知道标签中的内容在活动重新创建时是否会被恢复吗? - gunar

28

我们可以使用setTag()getTag()根据需要设置和获取自定义对象。 setTag()方法接受Object类型的参数,而getTag()返回一个Object对象。

例如:

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

22

对于网页开发人员来说,这似乎相当于数据...


16

这对于使用自定义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);
    }
            ....................
}

14

当你有一个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;
}

14
与ID不同,标签不用来识别视图。标签本质上是可以与视图关联的额外信息。它们通常被用作一种便利方式,将与视图相关的数据存储在视图本身中,而不是将其放在一个单独的结构中。
参考:http://developer.android.com/reference/android/view/View.html

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