什么是Overdraw镊辕?
Overdraw就是過(guò)度繪制,是指在一幀的時(shí)間內(nèi)(16.67ms)像素被繪制了多次蚁袭,理論上一個(gè)像素每次只繪制一次是最優(yōu)的,但是由于重疊的布局導(dǎo)致一些像素會(huì)被多次繪制石咬,而每次繪制都會(huì)對(duì)應(yīng)到CPU的一組繪圖命令和GPU的一些操作揩悄,當(dāng)這個(gè)操作耗時(shí)超過(guò)16.67ms時(shí),就會(huì)出現(xiàn)掉幀現(xiàn)象鬼悠,也就是我們所說(shuō)的卡頓删性,所以對(duì)重疊不可見(jiàn)元素的重復(fù)繪制會(huì)產(chǎn)生額外的開銷,需要盡量減少Overdraw的發(fā)生焕窝。
Android提供了測(cè)量Overdraw的選項(xiàng)蹬挺,在開發(fā)者選項(xiàng)-調(diào)試GPU過(guò)度繪制(Show GPU Overdraw),打開選項(xiàng)就可以看到當(dāng)前頁(yè)面Overdraw的狀態(tài)它掂,就可以觀察屏幕的繪制狀態(tài)巴帮。該工具會(huì)使用三種不同的顏色繪制屏幕,來(lái)指示overdraw發(fā)生在哪里以及程度如何虐秋,其中:
沒(méi)有顏色: 意味著沒(méi)有overdraw榕茧。像素只畫了一次。
藍(lán)色: 意味著overdraw 1倍客给。像素繪制了兩次用押。大片的藍(lán)色還是可以接受的(若整個(gè)窗口是藍(lán)色的,可以擺脫一層)靶剑。
綠色: 意味著overdraw 2倍蜻拨。像素繪制了三次池充。中等大小的綠色區(qū)域是可以接受的但你應(yīng)該嘗試優(yōu)化、減少它們缎讼。
淺紅: 意味著overdraw 3倍收夸。像素繪制了四次,小范圍可以接受休涤。
暗紅: 意味著overdraw 4倍咱圆。像素繪制了五次或者更多。這是錯(cuò)誤的功氨,要修復(fù)它們序苏。
那么我們?cè)趺磥?lái)消滅overdraw呢?總的原則就是:盡量避免重疊不可見(jiàn)元素的繪制捷凄,基于這個(gè)原則忱详,我們大概可以想出以下幾招:
第一招:合理選擇控件容器
既然overdraw是因?yàn)橹貜?fù)繪制了同一片區(qū)域的像素點(diǎn),那我們首先想到的是解決布局問(wèn)題跺涤。Android提供的Layout控件主要包括LinearLayout匈睁、TableLayout、FrameLayout桶错、RelativeLayout航唆。俗話說(shuō)條條大路通羅馬,同一個(gè)界面我們可以使用不同的容器控件來(lái)表達(dá)院刁,但是各個(gè)容器控件描述界面的復(fù)雜度是不一樣的糯钙。一般來(lái)說(shuō)LinearLayout最易,RelativeLayout較復(fù)雜退腥。但是尺有所短任岸,寸有所長(zhǎng),LinearLayout只能用來(lái)描述一個(gè)方向上連續(xù)排列的控件狡刘,而RelativeLayout幾乎可以用于描述任意復(fù)雜度的界面享潜。但是我又要說(shuō)但是了,表達(dá)能力越強(qiáng)的容器控件嗅蔬,性能往往略低一些剑按,因?yàn)橄到y(tǒng)需要將更多的時(shí)間花在計(jì)算子控件的位置上。綜上所述:LinearLayout易用购城,效率高吕座,表達(dá)能力有限。RelativeLayout復(fù)雜瘪板,表達(dá)能力強(qiáng)吴趴,效率稍遜。
那么對(duì)于同一界面而言侮攀,作為開發(fā)者考慮是使用盡量少的锣枝、表達(dá)能力強(qiáng)的RelativeLayout作為容器厢拭,還是選擇多個(gè)、表達(dá)能力稍弱的LinearLayout來(lái)展示撇叁。從減少overdraw的角度來(lái)看供鸠,LinearLayout會(huì)增加控件數(shù)的層級(jí),自然是RelativeLayout更優(yōu)陨闹,但是當(dāng)某一界面在使用LinearLayout并不會(huì)比RelativeLayout帶來(lái)更多的控件數(shù)和控件層級(jí)時(shí)楞捂,LinearLayout則是首選。所以在表達(dá)界面的時(shí)候趋厉,作為一個(gè)有前瞻性的開發(fā)者要根據(jù)實(shí)際情況來(lái)選擇合適容器控件寨闹,在保證性能的同時(shí),盡量避免overdraw君账。
第二招:去掉window的默認(rèn)背景
當(dāng)我們使用了Android自帶的一些主題時(shí)繁堡,window會(huì)被默認(rèn)添加一個(gè)純色的背景,這個(gè)背景是被DecorView持有的乡数。當(dāng)我們的自定義布局時(shí)又添加了一張背景圖或者設(shè)置背景色椭蹄,那么DecorView的background此時(shí)對(duì)我們來(lái)說(shuō)是無(wú)用的,但是它會(huì)產(chǎn)生一次Overdraw净赴,帶來(lái)繪制性能損耗绳矩。
去掉window的背景可以在onCreate()中setContentView()之后調(diào)用
getWindow().setBackgroundDrawable(null);
或者在theme中添加
android:windowbackground="null";
第三招:去掉其他不必要的背景
有時(shí)候?yàn)榱朔奖銜?huì)先給Layout設(shè)置一個(gè)整體的背景玖翅,再給子View設(shè)置背景埋酬,這里也會(huì)造成重疊,如果子View寬度mach_parent烧栋,可以看到完全覆蓋了Layout的一部分,這里就可以通過(guò)分別設(shè)置背景來(lái)減少重繪拳球。再比如如果采用的是selector的背景审姓,將normal狀態(tài)的color設(shè)置為“@android:color/transparent”,也同樣可以解決問(wèn)題。這里只簡(jiǎn)單舉兩個(gè)例子祝峻,我們?cè)陂_發(fā)過(guò)程中的一些習(xí)慣性思維定式會(huì)帶來(lái)不經(jīng)意的Overdraw魔吐,所以開發(fā)過(guò)程中我們?yōu)槟硞€(gè)View或者ViewGroup設(shè)置背景的時(shí)候酬姆,先思考下是否真的有必要浮定,或者思考下這個(gè)背景能不能分段設(shè)置在子View上匿又,而不是圖方便直接設(shè)置在根View上。
第四招:ClipRect & QuickReject
為了解決Overdraw的問(wèn)題,Android系統(tǒng)會(huì)通過(guò)避免繪制那些完全不可見(jiàn)的組件來(lái)盡量減少消耗桦他。但是不幸的是垃瞧,對(duì)于那些過(guò)于復(fù)雜的自定義的View(通常重寫了onDraw方法),Android系統(tǒng)無(wú)法檢測(cè)在onDraw里面具體會(huì)執(zhí)行什么操作,系統(tǒng)無(wú)法監(jiān)控并自動(dòng)優(yōu)化碳默,也就無(wú)法避免Overdraw了。但是我們可以通過(guò)canvas.clipRect()來(lái)幫助系統(tǒng)識(shí)別那些可見(jiàn)的區(qū)域。這個(gè)方法可以指定一塊矩形區(qū)域,只有在這個(gè)區(qū)域內(nèi)才會(huì)被繪制癣漆,其他的區(qū)域會(huì)被忽視。這個(gè)API可以很好的幫助那些有多組重疊組件的自定義View來(lái)控制顯示的區(qū)域剂买。同時(shí)clipRect方法還可以幫助節(jié)約CPU與GPU資源惠爽,在clipRect區(qū)域之外的繪制指令都不會(huì)被執(zhí)行,那些部分內(nèi)容在矩形區(qū)域內(nèi)的組件瞬哼,仍然會(huì)得到繪制婚肆。除了clipRect方法之外,我們還可以使用canvas.quickreject()來(lái)判斷是否沒(méi)和某個(gè)矩形相交坐慰,從而跳過(guò)那些非矩形區(qū)域內(nèi)的繪制操作较性。
第五招:ViewStub
ViewStub是個(gè)什么東西?一句話總結(jié):高效占位符结胀。
我們經(jīng)常會(huì)遇到這樣的情況赞咙,運(yùn)行時(shí)動(dòng)態(tài)根據(jù)條件來(lái)決定顯示哪個(gè)View或布局。常用的做法是把View都寫在上面糟港,先把它們的可見(jiàn)性都設(shè)為View.GONE攀操,然后在代碼中動(dòng)態(tài)的更改它的可見(jiàn)性。這樣的做法的優(yōu)點(diǎn)是邏輯簡(jiǎn)單而且控制起來(lái)比較靈活秸抚。但是它的缺點(diǎn)就是速和,耗費(fèi)資源。雖然把View的初始可見(jiàn)View.GONE但是在Inflate布局的時(shí)候View仍然會(huì)被Inflate剥汤,也就是說(shuō)仍然會(huì)創(chuàng)建對(duì)象颠放,會(huì)被實(shí)例化,會(huì)被設(shè)置屬性吭敢。也就是說(shuō)慈迈,會(huì)耗費(fèi)內(nèi)存等資源。
推薦的做法是使用android.view.ViewStub省有,ViewStub是一個(gè)輕量級(jí)的View,它一個(gè)看不見(jiàn)的谴麦,不占布局位置蠢沿,占用資源非常小的控件∝倚В可以為ViewStub指定一個(gè)布局舷蟀,在Inflate布局的時(shí)候,只有ViewStub會(huì)被初始化,然后當(dāng)ViewStub被設(shè)置為可見(jiàn)的時(shí)候野宜,或是調(diào)用了ViewStub.inflate()的時(shí)候扫步,ViewStub所向的布局就會(huì)被Inflate和實(shí)例化,然后ViewStub的布局屬性都會(huì)傳給它所指向的布局匈子。這樣河胎,就可以使用ViewStub來(lái)方便的在運(yùn)行時(shí),要還是不要顯示某個(gè)布局虎敦。
((ViewStub)?findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);
View?importPanel?=?((ViewStub)?findViewById(R.id.stub_view)).inflate();
第六招:Merge
Merge標(biāo)簽有什么用呢游岳?簡(jiǎn)單粗暴點(diǎn)回答:干掉一個(gè)view層級(jí)。
Merge的作用很明顯其徙,但是也有一些使用條件的限制胚迫。有兩種情況下我們可以使用Merge標(biāo)簽來(lái)做容器控件。第一種子視圖不需要指定任何針對(duì)父視圖的布局屬性唾那,就是說(shuō)父容器僅僅是個(gè)容器访锻,子視圖只需要直接添加到父視圖上用于顯示就行。另外一種是假如需要在LinearLayout里面嵌入一個(gè)布局(或者視圖)闹获,而恰恰這個(gè)布局(或者視圖)的根節(jié)點(diǎn)也是LinearLayout期犬,這樣就多了一層沒(méi)有用的嵌套,無(wú)疑這樣只會(huì)拖慢程序速度昌罩。而這個(gè)時(shí)候如果我們使用merge根標(biāo)簽就可以避免那樣的問(wèn)題哭懈。另外Merge只能作為XML布局的根標(biāo)簽使用,當(dāng)Inflate以開頭的布局文件時(shí)茎用,必須指定一個(gè)父ViewGroup遣总,并且必須設(shè)定attachToRoot為true。
第七招:善用draw9patch
給ImageView加一個(gè)邊框轨功,你肯定遇到過(guò)這種需求旭斥,通常在ImageView后面設(shè)置一張背景圖,露出邊框便完美解決問(wèn)題古涧,此時(shí)這個(gè)ImageView垂券,設(shè)置了兩層drawable,底下一層僅僅是為了作為圖片的邊框而已羡滑。但是兩層drawable的重疊區(qū)域去繪制了兩次菇爪,導(dǎo)致overdraw。
優(yōu)化方案: 將背景drawable制作成draw9patch柒昏,并且將和前景重疊的部分設(shè)置為透明凳宙。由于Android的2D渲染器會(huì)優(yōu)化draw9patch中的透明區(qū)域,從而優(yōu)化了這次overdraw职祷。 但是背景圖片必須制作成draw9patch才行氏涩,因?yàn)锳ndroid 2D渲染器只對(duì)draw9patch有這個(gè)優(yōu)化届囚,否則,一張普通的Png是尖,就算你把中間的部分設(shè)置成透明意系,也不會(huì)減少這次overdraw。
第八招:慎用Alpha
假如對(duì)一個(gè)View做Alpha轉(zhuǎn)化饺汹,需要先將View繪制出來(lái)蛔添,然后做Alpha轉(zhuǎn)化,最后將轉(zhuǎn)換后的效果繪制在界面上首繁。通俗點(diǎn)說(shuō)作郭,做Alpha轉(zhuǎn)化就需要對(duì)當(dāng)前View繪制兩遍,可想而知弦疮,繪制效率會(huì)大打折扣夹攒,耗時(shí)會(huì)翻倍,所以Alpha還是慎用胁塞。
如果一定做Alpha轉(zhuǎn)化的話咏尝,可以采用緩存的方式。
view.setLayerType(LAYER_TYPE_HARDWARE);
doSmoeThing();
view.setLayerType(LAYER_TYPE_NONE);
通過(guò)setLayerType方式可以將當(dāng)前界面緩存在GPU中啸罢,這樣不需要每次繪制原始界面编检,但是GPU內(nèi)存是相當(dāng)寶貴的,所以用完要馬上釋放掉扰才。
第九招:避免“OverDesign”
overdraw會(huì)給APP帶來(lái)不好的體驗(yàn)允懂,overdraw產(chǎn)生的原因無(wú)外乎:復(fù)雜的Layout層級(jí),重疊的View衩匣,重疊的背景這幾種蕾总。開發(fā)人員無(wú)節(jié)制的View堆砌,究其根本無(wú)非是產(chǎn)品無(wú)節(jié)制的需求設(shè)計(jì)琅捏。有道是“由儉入奢易生百,由奢入儉難”,很多APP披著過(guò)度設(shè)計(jì)的華麗外衣柄延,卻忘了簡(jiǎn)單易用才是王道的本質(zhì)蚀浆,紛繁復(fù)雜的設(shè)計(jì)并不會(huì)給用戶帶來(lái)好的體驗(yàn),反而會(huì)讓用戶有壓迫感搜吧,產(chǎn)品本身也有可能因此變得卡頓市俊。當(dāng)然,一切拋開業(yè)務(wù)談優(yōu)化都是空中樓閣滤奈,這就需要產(chǎn)品設(shè)計(jì)也要有一個(gè)權(quán)衡秕衙,在復(fù)雜的業(yè)務(wù)邏輯與簡(jiǎn)單易用的界面展現(xiàn)中做一個(gè)平衡,而不是一味的OverDesign僵刮。
引用網(wǎng)址:https://www.cnblogs.com/krislight1105/p/5352517.html