View类型对象的setTag()和getTag()等方法的主要目的是什么?

我认为我可以将任意数量的对象与单个视图相关联是正确的吗?


当前回答

我想补充几句话。

尽管在ViewHolder模式的特定情况下使用get/setTag(Object)似乎非常有用,但我建议在其他情况下使用它之前要三思。几乎总有其他设计更好的解决方案。

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

It is non-obvious for other developers what you designed to store as tag in the view. The methods setTag/getTag are not descriptive at all. It just stores an Object, which requires to be cast when you want to getTag. You can get unexpected crashes later when you decide to change the type of stored object in the tag. Here's a real-life story: We had a pretty big project with a lot of adapters, async operations with views and so on. One developer decided to set/getTag in his part of code, but another one had already set the tag to this view. In the end, someone couldn't find his own tag and was very confused. It cost us several hours to find the bug.

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

Prior to Android 4.0, the implementation of View.setTag(int, Object) would store the objects in a static map, where the values were strongly referenced. This means that if the object contains any references pointing back to the context, the context (which points to pretty much everything else) will leak. If you pass a view, the view provides a reference to the context that created it. Similarly, view holders typically contain a view, and cursors are sometimes also associated with views.

其他回答

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

进入getTag / setTag:

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

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

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

这基本上是一种让视图拥有记忆的方式。

当你有一个ListView并且想要回收/重用视图时,TAGs的设置非常有用。这样,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;
}

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

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

例如,

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

与id不同,标记不用于标识视图。标签本质上是可以与视图相关联的额外信息。它们最常用来方便地在视图本身中存储与视图相关的数据,而不是将它们放在一个单独的结构中。

参考:http://developer.android.com/reference/android/view/View.html