Android View中setTag的二三事

每一个APP,都离不开View的使用,小到一个登陆注册页面,大到复杂的网上商城,都是View使用的具体体现。

往往我们使用View,其实就是为了向用户展示一定的数据,因此,view的使用又总是离不开数据的。基本很多人的做法都会把数据以及view分开,但是其实在Android开发的view中已经有api接口可以完成一定量数据的存储了,这就是——View.setTag()以及View.getTag()

### 什么是setTag

setTag()是做什么的呢?SDK的解释为:

Unlike IDs, tags are not used to identify views. Tags are essentially an extra piece of information that can be associated with a view. They are most often used as a convenience to store data related to views in the views themselves rather than by putting them in a separate structure.

Tag从本质上来讲是就是相关联的view的额外的信息。它们经常用来存储一些view的数据,这样做非常方便而不用存入另外的单独结构。

###setTag的一些使用场景

setTag的这个api其实隐藏的比较的深,甚至在笔者之前学习的基础教程中是完全没有提到过的(好吧,也有可能是我的基础教材选的不好)。我第一次接触这一个api是在自己在尝试的完成自己的课程设计项目时,在项目中使用了**ListView**展示一系列的数据,当时的数据量是比较的大,同时每一个listItem中的view也是比较的复杂,不仅有头像,有用户名,有标题,有内容,甚至是有图片等等。

复杂的view的创建是会比较消耗机器的性能的,大量复杂的view就不用说了。因此我便在寻找方法优化list的时候,接触了setTag().在接触Android开发以来,setTag()使用的最多的场景基本就是ListView,GridView,RecycleView,ViewPage,Gallery啊等等一系列类似用于展示大量重复的View的控件上。*因为这些控件的数据量一般会比较多,例如在夜夜项目中照片瀑布流中,一页就是30张照片,加载十几页就是几百张的图片,如果不对资源加以回收的话,即使是刘威的一加3有6G内存也都不够用~*

因此,这一些控件基本都会对视图判断是否在可视的范围,从而决定回不回收资源。**那么,问题就来了,当被回收的view再一次出现在世人的眼前的时候,机器是不是需要重新绑定数据,重新加载资源,重新绘制view?**问题的答案是肯定的,**当然要!**(都已经被回收了)那么明明已经使用过的view再来一次也要重新开始绘制,那样不是一直在重复同样的动作?**特别是在一些列表或是网格的视图中,明明每一个item都是长得一样的,也是要重复创建?**同样的事,重复的做,那就是在无谓的消耗机器性能了。**因此,我们要做的是让同样的事,只做一次就好!**

通过观察,我们可以发现,其实无论是ListView,GridView,RecycleView,ViewPage,还是Gallery,它们的使用都是离不开adapter,每一个子item的创建也是在adapter中的getView()中完成的,因此,我们**要开刀的就是adapter的getView()**.

在夜夜项目中的经理列表,如下:

Android View中setTag的二三事_第1张图片

![](/content/images/2016/07/726155124686308457.jpg)

每一个itemView的布局都是一样的,都包括了经理头像,名字,常去酒吧,性别,热度,驻场,以及三张小的现场图片。那么,该经理listView的adapter中getView()最正常的写法如下:

Android View中setTag的二三事_第2张图片

![](/content/images/2016/07/----_20160722011238.png)

其实,在itemView相同的情况下,每一次创建itemView的时候都要重复的去绑定控件,须知,项目中R文件中的id可是非常very多的,这样重复的findViewById是很消耗性能的,特别在控件还很多,一个list中的itemView也很多的情况下。那么,我们就要对绑定控件这一部分进行优化。

首先我们通过查询setTag()的源码就可以知道

Android View中setTag的二三事_第3张图片

![](/content/images/2016/07/----_20160722011826.png)

setTag()是把Object对象作为参数对view进行存储的。也就是说,我们要把一个itemView中的控件抽取成一个Object,在此,我们创建了一个ViewHolder

Android View中setTag的二三事_第4张图片

![](/content/images/2016/07/----_20160722012050.png)

然后修改getView()

Android View中setTag的二三事_第5张图片

![](/content/images/2016/07/----_20160722012234.png)

在第一次创建itemView的时候,完成对控件的绑定,同时吧控件作为一个object--holder,把它通过setTag()存到itemView中,再第二次使用的时候就可以通过getTag()把holder取出来直接使用,也就是说,在list中itemView相同的情况下,我们只进行了一次的控件资源绑定。是不是就省了很多?

**这样的方式是适合用所有类似的控件的。包括上文提及的GridView,RecycleView,ViewPage,Gallery等,具体可以自己发挥**

### 那么,问题又来了!

在夜夜项目中开发在线聊天IM的时候,**聊天界面的消息也是由listView实现的,但是,ItemView长得不一样啊???itemView有文本消息的,有图片消息的,也有分享的名片、酒水套餐等,一个ViewHolder根本hold不住了喂!有可能上一次创建的文本消息itemView,这一次却是要图片消息的itemView了!**

其实,这个问题根本**不用担心!!!**我们翻一翻源码又发现,还有这个东西:setTag(key,object)

Android View中setTag的二三事_第6张图片

![](/content/images/2016/07/----_20160722013426.png)

也就说,其实一个view中是可以setTag多个object的,我们只需要在setTag的时候指定key,只有再通过getTag(key)就可以取出相对应的object了,界面再多类型的itemView,再多不同的holder也不怕了呢~

那么我为什么又要拿出来单独说呢?因为**这里面有坑!**

假如你这样子使用:

> `converView.setTag("text",textHolder);`

`converView.setTag("pic",picHolder);`

`converView.setTag(100,shareHolder);`

>

那么,恭喜你,你的项目要开始Crash了,开始抛出IllegalArgumentException的问题了~因为在SDK中有这样子说过:

>*“ The specified key should be an id declared in the resources of the application to ensure it is unique (see the ID resource type). Keys identified as belonging to the Android framework or not associated with any package will cause an IllegalArgumentExceptionto be thrown.”*

**也就是说你的key必须是唯一的!!!**

那么我们要怎么实现唯一的key呢?其实我们可以把key定义在资源文件中,再由编译器生成对应的,唯一的id!因此我们可以这样写在项目的资源res/values/string.xml中:

Android View中setTag的二三事_第7张图片

![](/content/images/2016/07/----_20160722014903.png)

然后直接:

`converView.setTag(R.id.tag_text,textHolder);`

就可以了,取值的时候:

`textHolder = converView.getTag(R.id.tag_text);`

然后,你就可以愉快的撸代码啦~

###### 至此,全文基本完!

*edit by: 陈育生*

如果这篇文章又帮到你的话,请点一下‘喜欢’,我会更努力的创作的

你可能感兴趣的:(Android View中setTag的二三事)