本文译自: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)和它的父对象都会再次执行绘图代码。