每一個(gè)APP,都離不開(kāi)View的使用,小到一個(gè)登陸注冊(cè)頁(yè)面,大到復(fù)雜的網(wǎng)上商城,都是View使用的具體體現(xiàn)。
往往我們使用View,其實(shí)就是為了向用戶(hù)展示一定的數(shù)據(jù),因此,view的使用又總是離不開(kāi)數(shù)據(jù)的。基本很多人的做法都會(huì)把數(shù)據(jù)以及view分開(kāi),但是其實(shí)在Android開(kāi)發(fā)的view中已經(jīng)有api接口可以完成一定量數(shù)據(jù)的存儲(chǔ)了,這就是——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從本質(zhì)上來(lái)講是就是相關(guān)聯(lián)的view的額外的信息。它們經(jīng)常用來(lái)存儲(chǔ)一些view的數(shù)據(jù),這樣做非常方便而不用存入另外的單獨(dú)結(jié)構(gòu)。
###setTag的一些使用場(chǎng)景
setTag的這個(gè)api其實(shí)隱藏的比較的深,甚至在筆者之前學(xué)習(xí)的基礎(chǔ)教程中是完全沒(méi)有提到過(guò)的(好吧,也有可能是我的基礎(chǔ)教材選的不好)。我第一次接觸這一個(gè)api是在自己在嘗試的完成自己的課程設(shè)計(jì)項(xiàng)目時(shí),在項(xiàng)目中使用了**ListView**展示一系列的數(shù)據(jù),當(dāng)時(shí)的數(shù)據(jù)量是比較的大,同時(shí)每一個(gè)listItem中的view也是比較的復(fù)雜,不僅有頭像,有用戶(hù)名,有標(biāo)題,有內(nèi)容,甚至是有圖片等等。
復(fù)雜的view的創(chuàng)建是會(huì)比較消耗機(jī)器的性能的,大量復(fù)雜的view就不用說(shuō)了。因此我便在尋找方法優(yōu)化list的時(shí)候,接觸了setTag().在接觸Android開(kāi)發(fā)以來(lái),setTag()使用的最多的場(chǎng)景基本就是ListView,GridView,RecycleView,ViewPage,Gallery啊等等一系列類(lèi)似用于展示大量重復(fù)的View的控件上。*因?yàn)檫@些控件的數(shù)據(jù)量一般會(huì)比較多,例如在夜夜項(xiàng)目中照片瀑布流中,一頁(yè)就是30張照片,加載十幾頁(yè)就是幾百?gòu)埖膱D片,如果不對(duì)資源加以回收的話(huà),即使是劉威的一加3有6G內(nèi)存也都不夠用~*
因此,這一些控件基本都會(huì)對(duì)視圖判斷是否在可視的范圍,從而決定回不回收資源。**那么,問(wèn)題就來(lái)了,當(dāng)被回收的view再一次出現(xiàn)在世人的眼前的時(shí)候,機(jī)器是不是需要重新綁定數(shù)據(jù),重新加載資源,重新繪制view?**問(wèn)題的答案是肯定的,**當(dāng)然要!**(都已經(jīng)被回收了)那么明明已經(jīng)使用過(guò)的view再來(lái)一次也要重新開(kāi)始繪制,那樣不是一直在重復(fù)同樣的動(dòng)作?**特別是在一些列表或是網(wǎng)格的視圖中,明明每一個(gè)item都是長(zhǎng)得一樣的,也是要重復(fù)創(chuàng)建?**同樣的事,重復(fù)的做,那就是在無(wú)謂的消耗機(jī)器性能了。**因此,我們要做的是讓同樣的事,只做一次就好!**
通過(guò)觀察,我們可以發(fā)現(xiàn),其實(shí)無(wú)論是ListView,GridView,RecycleView,ViewPage,還是Gallery,它們的使用都是離不開(kāi)adapter,每一個(gè)子item的創(chuàng)建也是在adapter中的getView()中完成的,因此,我們**要開(kāi)刀的就是adapter的getView()**.
在夜夜項(xiàng)目中的經(jīng)理列表,如下:


每一個(gè)itemView的布局都是一樣的,都包括了經(jīng)理頭像,名字,常去酒吧,性別,熱度,駐場(chǎng),以及三張小的現(xiàn)場(chǎng)圖片。那么,該經(jīng)理listView的adapter中g(shù)etView()最正常的寫(xiě)法如下:


其實(shí),在itemView相同的情況下,每一次創(chuàng)建itemView的時(shí)候都要重復(fù)的去綁定控件,須知,項(xiàng)目中R文件中的id可是非常very多的,這樣重復(fù)的findViewById是很消耗性能的,特別在控件還很多,一個(gè)list中的itemView也很多的情況下。那么,我們就要對(duì)綁定控件這一部分進(jìn)行優(yōu)化。
首先我們通過(guò)查詢(xún)setTag()的源碼就可以知道


setTag()是把Object對(duì)象作為參數(shù)對(duì)view進(jìn)行存儲(chǔ)的。也就是說(shuō),我們要把一個(gè)itemView中的控件抽取成一個(gè)Object,在此,我們創(chuàng)建了一個(gè)ViewHolder


然后修改getView()


在第一次創(chuàng)建itemView的時(shí)候,完成對(duì)控件的綁定,同時(shí)吧控件作為一個(gè)object--holder,把它通過(guò)setTag()存到itemView中,再第二次使用的時(shí)候就可以通過(guò)getTag()把holder取出來(lái)直接使用,也就是說(shuō),在list中itemView相同的情況下,我們只進(jìn)行了一次的控件資源綁定。是不是就省了很多?
**這樣的方式是適合用所有類(lèi)似的控件的。包括上文提及的GridView,RecycleView,ViewPage,Gallery等,具體可以自己發(fā)揮**
### 那么,問(wèn)題又來(lái)了!
在夜夜項(xiàng)目中開(kāi)發(fā)在線聊天IM的時(shí)候,**聊天界面的消息也是由listView實(shí)現(xiàn)的,但是,ItemView長(zhǎng)得不一樣?。???itemView有文本消息的,有圖片消息的,也有分享的名片、酒水套餐等,一個(gè)ViewHolder根本hold不住了喂!有可能上一次創(chuàng)建的文本消息itemView,這一次卻是要圖片消息的itemView了!**
其實(shí),這個(gè)問(wèn)題根本**不用擔(dān)心?。?!**我們翻一翻源碼又發(fā)現(xiàn),還有這個(gè)東西:setTag(key,object)


也就說(shuō),其實(shí)一個(gè)view中是可以setTag多個(gè)object的,我們只需要在setTag的時(shí)候指定key,只有再通過(guò)getTag(key)就可以取出相對(duì)應(yīng)的object了,界面再多類(lèi)型的itemView,再多不同的holder也不怕了呢~
那么我為什么又要拿出來(lái)單獨(dú)說(shuō)呢?因?yàn)?*這里面有坑!**
假如你這樣子使用:
> `converView.setTag("text",textHolder);`
`converView.setTag("pic",picHolder);`
`converView.setTag(100,shareHolder);`
>
那么,恭喜你,你的項(xiàng)目要開(kāi)始Crash了,開(kāi)始拋出IllegalArgumentException的問(wèn)題了~因?yàn)樵赟DK中有這樣子說(shuō)過(guò):
>*“ 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.”*
**也就是說(shuō)你的key必須是唯一的?。?!**
那么我們要怎么實(shí)現(xiàn)唯一的key呢?其實(shí)我們可以把key定義在資源文件中,再由編譯器生成對(duì)應(yīng)的,唯一的id!因此我們可以這樣寫(xiě)在項(xiàng)目的資源res/values/string.xml中:


然后直接:
`converView.setTag(R.id.tag_text,textHolder);`
就可以了,取值的時(shí)候:
`textHolder = converView.getTag(R.id.tag_text);`
然后,你就可以愉快的擼代碼啦~
###### 至此,全文基本完!
*edit by: 陳育生*