Android 4.0硬件加速及繪制技巧
從Android 3.0開(kāi)始,Android 2D的繪制流程就設(shè)計(jì)為能夠更好地支持硬件加速。使用GPU的View在Canvas上進(jìn)行畫(huà)的操作時(shí)都會(huì)使用硬件加速。在最新的Android版本里,圖形硬件加速及繪制技巧得到了更好的提升.
Android 4.0
1.Android 4.0硬件加速的使用
1.1 硬件加速的控制級(jí)別
啟用硬件加速的最簡(jiǎn)單方法就是為整個(gè)系統(tǒng)打開(kāi)硬件加速的全局設(shè)置。如果你的程序是標(biāo)準(zhǔn)View或者是Drawable 則硬件加速的全局設(shè)這并不會(huì)造成不良的影響。然而硬件加速并不支持所有2D畫(huà)的操作,所以開(kāi)啟硬件加速可能會(huì)對(duì)使用自定義組件的應(yīng)用程序造成影響,問(wèn)題常常表現(xiàn)在不可見(jiàn)的元素異常和錯(cuò)誤的像素渲染,為了解決這個(gè)問(wèn)題Android可以讓你選擇啟動(dòng)或者禁用以下級(jí)別的硬件加速:Application Activity Window 和 View 。
1.1.1 Application級(jí)別
在你的Android Manifest文件中添加 屬性標(biāo)記,以便為整個(gè)應(yīng)用程序使用硬件加速。
1.1.2 Activity級(jí)別
如果你的應(yīng)用程序不能在Application應(yīng)用級(jí)別表現(xiàn)良好的話,則可以使用對(duì)Activity進(jìn)行單獨(dú)控制。要啟動(dòng)或者禁用一個(gè)Activity的硬件加速,你可以使用activity的android:hardwareAccelerated屬性。下面的一個(gè)列子使整個(gè)Application啟用硬件加速,但是對(duì)一個(gè)Activity禁止使用硬件加速。
1.1.3 Window級(jí)別
如果你需要更細(xì)粒度的控制,你可以通過(guò)如下代碼給window進(jìn)行加速。
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
注意:現(xiàn)階段你不能在Window級(jí)別對(duì)它禁用硬件加速。
1.1.4 View級(jí)別
我們可以對(duì)單獨(dú)的View在運(yùn)行時(shí)階段禁用硬件加速。我們可以使用如下代碼:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
注意:現(xiàn)階段不能夠在View級(jí)別進(jìn)行硬件加速。
1.2 判斷一個(gè)View是否已經(jīng)啟用了硬件加速
有時(shí)候我們需要知道一個(gè)應(yīng)用程序是否已經(jīng)啟用了硬件加速,特別是針對(duì)一些自定義控件。因?yàn)槟愕膽?yīng)用程序做了很多自定義“畫(huà)”的操作,但并不是所有的過(guò)程都支持新的“畫(huà)”的渲染過(guò)程。
有兩種不同的方法來(lái)檢查Application是否啟用了硬件加速:
1.2.1 使用View.isHardwareAccelerated() 如果返回true則可以說(shuō)明這個(gè)View所在的窗口已經(jīng)啟用了硬件加速。
1.2.2 Canvas.isHardwareAccelerated() 如果返回true則說(shuō)明這個(gè)Canvas已經(jīng)啟用了硬件加速。
如果你必須要在你的繪畫(huà)代碼中進(jìn)行是否已經(jīng)加速的檢查,如果可能的話請(qǐng)使用Canvas.isHardwareAccelerated()來(lái)代替View.isHardwareAccelerated()。當(dāng)一個(gè)View是存在于一個(gè)已經(jīng)加速的Windows上時(shí),任然可以使用沒(méi)有硬件加速的Canvas進(jìn)行繪畫(huà),這場(chǎng)發(fā)生在,比如,當(dāng)我們把一個(gè)View畫(huà)到Bitmap上然后用作緩存。
2.Android 4.0的繪制模型
當(dāng)開(kāi)啟了硬件加速,Android框架將會(huì)使用一種新的繪制模型,這種模型將會(huì)使用顯示列表把你的應(yīng)用顯示到屏幕上。要完全理解顯示列表和他們?nèi)绾斡绊懩愕膽?yīng)用程序,理解Android 4.0如何在非硬件加速的情況下如何繪制Views是很有必要的,下面將分別介紹軟件加速和硬件加速。
2.1基于軟件的繪制模型
在基于軟件繪制模型中,View的繪制遵循以下兩步:1.使整個(gè)控件層級(jí)無(wú)效。2.對(duì)層級(jí)進(jìn)行繪制。
當(dāng)一個(gè)應(yīng)用程序需要更新它UI的一部分時(shí),它將會(huì)調(diào)用內(nèi)容發(fā)生改變的View的invalidate()方法(或者invalidate的變體)。Invalidate的消息按照View的層級(jí)關(guān)系向上傳遞用以計(jì)算需要重畫(huà)的部分(即臟區(qū)域)。然后Android系統(tǒng)會(huì)對(duì)和臟區(qū)域有交集的所有View進(jìn)行繪制,不幸的是這種模型中有兩個(gè)缺點(diǎn):
2.1.1 在這種模型中當(dāng)在不同的層進(jìn)行畫(huà)的時(shí)候,會(huì)額外執(zhí)行很多代碼。例如一個(gè)Button是位于另外一個(gè)View之上,當(dāng)對(duì)Button調(diào)用 Invalidate()時(shí),Android就會(huì)對(duì)這個(gè)View進(jìn)行重繪,即便這個(gè)View沒(méi)有發(fā)生任何變化。
2.1.2 第二個(gè)問(wèn)題是這種繪制模型會(huì)隱藏你Application中的Bug。因?yàn)锳ndroid系統(tǒng)會(huì)對(duì)和臟區(qū)域有交集的View進(jìn)行重繪,在這種情況下如果一個(gè)view的內(nèi)容發(fā)生了改變,即便這個(gè)View的Invalidate()的方法并沒(méi)有得到調(diào)用,它也可能被重繪。你便會(huì)依賴調(diào)用了invalidate()的其他的控件以便獲得正確的行為,因此每當(dāng)你的Application發(fā)生改變時(shí),這種行為多要隨之發(fā)生改變。也是基于次因,在你的自定義控件中你必須不斷地調(diào)用invalidate()方法,當(dāng)你的數(shù)據(jù)或者是狀態(tài)會(huì)影響View的繪制代碼時(shí)。
注意:Android的View當(dāng)它們的屬性發(fā)生改變時(shí)會(huì)自動(dòng)的調(diào)用Invalidate()。比如,你改變一個(gè) Textview的背景或者是它的文本。
2.2 基于硬件加速模型
Android 系統(tǒng)仍然通過(guò)invalidate()和draw()去請(qǐng)求屏幕更新和重新渲染,但是實(shí)際處理畫(huà)的方式是不同的。不是立即執(zhí)行畫(huà)的命令,Android而會(huì)將所有畫(huà)的命令記錄在一個(gè)顯示列表里面,這個(gè)顯示列表包含了輸出的View層級(jí)的繪制代碼。還有一個(gè)優(yōu)化就是Android在顯示列表中只會(huì)記錄和更新顯示層級(jí)中通過(guò)調(diào)用invalidate()函數(shù)被標(biāo)記為“臟”的view。沒(méi)有被請(qǐng)求刷新的view可以通過(guò)重新請(qǐng)求先前的顯示列表以便重畫(huà)。新的繪制模型包括有三個(gè)步驟:1.禁用整個(gè)View層級(jí)。2.記錄和更新顯示列表。3.繪制顯示列表。
使用這個(gè)模型你不能依賴一個(gè)View和臟區(qū)域有交集就會(huì)執(zhí)行draw()方法。要確保Android系統(tǒng)記錄了一個(gè)View的顯示列表,你必須調(diào)用invalidate()方法,如果忘記了調(diào)用刷新,會(huì)使View即便是發(fā)生了改變后也會(huì)看起來(lái)相同,這是一個(gè)比較容易發(fā)現(xiàn)bug的方式。
使用顯示列表的方式對(duì)動(dòng)畫(huà)的表現(xiàn)也是很有好處的,因?yàn)樵O(shè)置指定的屬性值,比如透明度或者旋轉(zhuǎn),就不需要請(qǐng)求刷新目標(biāo)View(這將自動(dòng)執(zhí)行)。這項(xiàng)優(yōu)化也應(yīng)用于有顯示列表的Views(啟用了硬件加速的View),例如,現(xiàn)在有一個(gè)LinearLayout包含了一個(gè)ListView和Button,listview在button的上面。這時(shí)候LinearLayout的顯示列表如下所示:
◆DrawDisplayList(ListView) ;
◆DrawDisplayList(Button) ;
假設(shè)你現(xiàn)在你想更新這個(gè)Listview的不透明度,在設(shè)置Listview的 setAlpha(0.5f) 屬性之后,LinearLayout的顯示列表應(yīng)該包含如下:
◆ SaveLayerAlpha(0.5)
◆ DrawDisplayList(ListView)
◆ Restore
◆ DrawDisplayList(Button)
這時(shí)候繪制Listview的復(fù)雜過(guò)程就會(huì)省略了,取而代之的是簡(jiǎn)單的更新了LinearLayout的顯示列表。如果一個(gè)應(yīng)用程序并沒(méi)有啟用硬件加速,Listview和它的父view的畫(huà)的代碼都會(huì)重新執(zhí)行。
3.Android 4.0 View的層
3.1層的分類
所有的Android版本都有能力對(duì)離屏緩沖進(jìn)行渲染,或者是使用View的繪制緩沖,或者是使用Canvas.saveLayer()函數(shù)。離屏緩沖或者Layer能夠有很多種應(yīng)用,例如能使處理復(fù)雜view的動(dòng)畫(huà)效果或者應(yīng)用一些合成效果都有更好地表現(xiàn)。例如你可以通過(guò)Canvas.saveLayer()的方式來(lái)對(duì)View做一個(gè)漸入漸出效果同時(shí)把它渲染到Layer中,然后再加上不透明效果合成后顯示到屏幕上。
由Android 3.0開(kāi)始你就能夠通過(guò)View.setLayerType()方法對(duì)何時(shí)以及如何使用層有了更多的控制,這個(gè)API具有兩個(gè)參數(shù)一個(gè)是你想使用的層類型,另外一個(gè)是可選參數(shù)Paint表明了Layer是如何被疊加的。你可以把Paint參數(shù)應(yīng)用到顏色過(guò)濾上,特別是混合模式或者是對(duì)一個(gè)layer進(jìn)行不透明效果。一個(gè)View可以使用如下的三種layer類型之一:
◆ LAYER_TYPE_NONE: 這個(gè)View將被按普通的方式進(jìn)行渲染,但是不會(huì)返回一個(gè)離屏的緩沖,這個(gè)是默認(rèn)的行為。
◆ LAYER_TYPE_HARDWARE:如果這個(gè)應(yīng)用被硬件加速的話,這個(gè)View將會(huì)在硬件中渲染為硬件紋理,如果應(yīng)用程序并沒(méi)有被硬件加速,則其效果和LAYER_TYPE_SOFTWARE是相同的。
◆ LAYER_TYPE_SOFTWARE: 此View 通過(guò)軟件渲染為一個(gè)bitmap。
3.2 層的使用
使用層的類型取決于你的目的:
3.2.1 性能:使用硬件層來(lái)渲染一個(gè)View成為硬件紋理。一旦一個(gè)View被渲染為一個(gè)層,它的繪制代碼將不會(huì)得到執(zhí)行,直到你調(diào)用了invalidate()函數(shù)。對(duì)于一些動(dòng)畫(huà),比如透明動(dòng)畫(huà)可以直接應(yīng)用到一個(gè)層上,這是GPU最有效率的使用方式。
3.2.2 顯示效果:使用硬件或者軟件層和Paint來(lái)對(duì)一個(gè)View進(jìn)行特殊的視覺(jué)處理,例如你可以對(duì)一個(gè)View通過(guò)使用ColorMatrixColorFilter來(lái)實(shí)現(xiàn)黑白效果。