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

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


当前回答

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

其他回答

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

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

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

我想补充几句话。

尽管在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.

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