本文譯自:http://developer.android.com/guide/topics/graphics/hardware-accel.html
判斷一個View對象是否被硬體加速
有些時候,尤其是對於那些定製的View對象,應用程式知道當前的View對象是否被硬體加速是十分有益的。如果應用程式做了很多定製的繪圖操作,並且不是所有的操作都會被新的渲染管道所支援,那麼這種判斷就特別有用。
有兩種不同的方法來檢查應用程式是否被硬體加速了:
1. 如果一個View對象跟一個被硬體加速的視窗綁定,那麼View.isHardwareAccelerated()方法就會返回true;
2. 如果一個Canvas對象被硬體加速了,那麼Canvas.isHardwareAccelerated()方法就會返回true。
如果必須要在繪圖代碼中做這種檢查,那麼在可能的情況下,要使用Canvas.isHardwareAccelerated()方法來代替View.isHardwareAccelerated()方法。當一個View對象跟一個被硬體加速的視窗綁定的時候,它依然能夠使用使用一個非硬體加速的Canvas對象。例如,把一個View對象繪製到緩衝中的一個位元影像時就會發生這種情況。
Android的繪圖模式
當硬體加速被啟用時,Android架構會採用一個新的繪圖模式,這種模式利用顯示列表把應用程式呈現在螢幕上。要充分理解顯示列表,以及它們是如何影響應用程式的,對於理解Android是如何繪製沒有硬體加速的View對象是有益的。下面分別介紹基於軟體的和硬體加速的繪圖模式。
基於軟體的繪圖模式
在軟體的繪圖模式中,View對象是通過以下兩個步驟來繪製的:
1. 讓View階層失效;
2. 繪製View階層。
無論何時,應用程式需要更新它的UI部分時,都回調用發生內容改變的View對象的invalidate()方法。無效的訊息請求會在View對象階層中傳遞,以便計算出需要重繪的螢幕地區(髒區)。然後,Android系統會在View階層中繪製所有的跟髒區相交的地區。不幸的是,這種方法有兩個缺點:
1. 這種模式在每個繪圖傳遞中需要很多的代碼執行。例如,如果應用程式調用了一個按鈕的invalidate()方法,並且該按鈕位於另一個View對象的上方,那麼即使該View對象沒有變化,那麼Android系統也要重新繪製它。
2. 第二個問題是這個種繪圖模式能夠隱藏應用程式中的bug。因為當View對象跟髒區相交時,Android系統就會重新繪製它,所以即使沒有調用View對象上的invalidate()方法,那麼View對象內容的改變也可能會導致它被重繪。當發生這種情況時,就要依賴另一個被失效的View對象來擷取正確的行為。這種行為能夠改變每次你對應用程式的修改。因為這個原因,在修改影響View對象的繪圖代碼的資料和狀態是,應該始終調用該定製View對象的invalidate()方法。
注意:在View對象的屬性發生變化時,如背景色或TextView對象中的文本等,Android系統會自動的調用該View對象的invalidate()方法。
硬體加速繪圖模式
這種模式下,Android系統依然會使用invalidate()方法和draw()方法來請求螢幕更新和展現View對象,但是實際的繪圖處理是不同的,它會立即執行繪圖命令,Android系統把這些命令記錄在內部的顯示列表中,列表中包含了View對象階層的繪圖代碼的輸出。另一種最佳化是:Android系統只需要針對由invalidate()方法調用所標記的View對象的髒區進行記錄和更新顯示列表。沒有失效的View對象能夠通過重新發布先前被記錄的顯示列表來進行簡單的重繪工作。這種繪圖模式包含三個階段:
1. 讓View的階層失效;
2. 記錄和更新顯示列表;
3. 繪製顯示列表。
使用這種模式,不能夠依賴相交的髒區View的draw()方法來執行繪圖工作。要確保Android系統記錄一個View對象的顯示列表,就必須調用invalidate()方法,如果忘記調用該方法,那麼在變化發生後,View對象看上去會跟變化之前相同,這是一個比較容易發現的Bug。
使用顯示列表對提升動畫的效能也是有好處的,因為設定諸如透明度、旋轉等屬性時,不需要讓目標View對象失效(系統會自動做這件事)。這種最佳化還適用於帶有顯示列表的View對象(應用程式被硬體加速時的任意View對象)。例如,假設有一個包含了一個Button對象的ListView對象的LinearLayout布局,那麼LinearLayout布局的顯示列表如下:
1. DrawDisplayList(ListView);
2. DrawDisplayList(Button)。
假設現在要改變ListView對象的不透明度,那麼在調用ListView對象的setAlpha(0.5f)方法時,顯示列表就包含了以下處理:
1. SaveLayerAlpha(0.5);
2. DrawDisplayList(ListView);
3. Restore;
4. DrawDisplayList(Button).
這裡沒有執行複雜的ListView對象的繪圖代碼。相反,系統只是比較簡單的更新了LinearLayout對象的顯示列表。在一個沒有啟用硬體加速的應用程式中,該列表(ListView)和它的父物件都會再次執行繪圖代碼。