View layers
在所有版本的Android中,views都有画到离屏缓冲的能力,这包括使用view的绘制cache,或使用Canvas.saveLayer().离屏缓冲,或者说层,有很多用处.你可以使用它们来为复杂的view动画或使用组合效果时提高性能.例如,你可以使用Canvas.saveLayer()实现淡出效果,这个方法会临时的把一个view画到一个layer中然后使用一个透明系数把它组合回屏幕上.
从Android3.0 (API level11)开始,你对何时以及如何通过View.setLayerType()方法来使用layer有了更多的控制能力.此方法有两个参数:你想使用的layer的类型和一个可选的Paint对象,这个对象描述了layer应被如何组合.你可以使用Paint参数来应用颜色过滤,或指定混合模式或不透明度到一个layer.view可以使用以下三个类型之一:
-
LAYER_TYPE_NONE:view按一般方式绘制,不使用离屏缓冲.这是默认的行为.
-
LAYER_TYPE_HARDWARE:如果应用被硬加速了,view会被绘制到一个硬件纹理中.如果应用没被硬加速,此类型的layer的行为同于LAYER_TYPE_SOFTWARE.
-
LAYER_TYPE_SOFTWARE:view被绘制到一个bitmap中.
要使用哪种layer类型取决于你的目标:
-
性能:使用一个硬件layer类型把view画到硬件纹理中.当一个view被画到一个layer中,它的绘制代码直到调用invalidate()时才会被执行.一些动画,比如透明变化的动画,可以直接在layer上应用,这对GPU来说轻而易举.
-
视觉效果:使用硬件或软件layer类型和一个Paint将视觉特效应用到view上.比如,你可以在后台用ColorMatrixColorFilter以黑白色画一个view.
-
兼容性:使用一个软件layer类型来强制view以软件方式绘制.如果一个view是硬加速的(比如,如果你的整个应用是硬加速的),但呈现时出现问题,改用软件方式绘制是一个避开硬件呈现管道限制的最简单的方式.
View layers 和动画
当你的应用被硬加速时,硬件layer可以提供更快更平滑的动画.当向一个有很多绘制操作的复杂的view应用动画时,要永远保证以60fps的速度运行是不可能的.但这可以通过使用硬件layers把view呈现到硬件纹理中来减轻一些负担.硬件纹理之后可以被用来产生动画,避免view为了产生动画而不断的重画自己.view在其属性发生改变而调用invalidate()或你主动调用invalidate()之前不会重画,如果你正在你的应用运行动画但是并不能获得平滑的效果,那么可以考虑在你的动画view上启用硬件layer.
当一个view被硬件layer所支持,它的一些属性的处理就跟layer混合到屏幕上的方式相关了.设置这些属性的操作将变得更高效,因为它们不再导致view变得无效再重绘了.下面列出的就是这些属性,调用这些属性的设置器时会对invalidation操作产生最佳的优化并且不再重画目标view:
-
alpha:改变layer的透明度
-
x, y,translationX, translationY:改变layer的位置
-
scaleX,scaleY:改变layer的大小
-
rotation,rotationX, rotationY: 在3D空间中改变layer的方向.
-
pivotX,pivotY: 改变layer的变换原点坐标.
这些属性是在使用一个ObjectAnimator为view产生动画时使用的.如果你想操作这些属性,调用适当的setter或getter即可.例如,要改变透明属性,调用setAlpha().下面的代码片段演示了让一个View绕Y轴进行三维变换的最佳方法:
因为硬件 layers 使用显卡内存,所以强烈建议你只在动画期间使用它们并在动画结束后禁止它们.你可以使用动画侦听器来做到:
提示和技巧
切换到2D图形硬加速能立即提升性能,但是你仍然需要按照以下建议来有效使用GPU:
-
减少你应用中views的数量
系统画的views越多,就越慢.这对软件呈现管线来说也是一样.减少views是优化你的UI的最简单的办法.
-
避免过度绘制
不要在彼此的上面画太多layers.移除那些完全被别的不透明view遮盖的view们.如果你需要把多个layer混合画到每个view的上面,应考虑把它们合并到一个layer中.对于当前硬件的一个好原则是画的次数不要超过每帧屏幕上像素的2.5倍(透明像素按位图计数).
-
不要在绘制方法们中创建render对象
一个常见的错误就是在每次调用绘制方法时都创建新的Paint或Path.这强制垃圾收集器运行得更频繁,并且导致高速缓存和硬件管道优化不起作用.
-
不要太频繁地修改shapes
比如混合shapes,paths,和circles时,是使用纹理遮罩呈现的.每次你创建或修改一个path,硬件管道都创建一个新的遮罩,这个代价是很昂贵的.
-
不要太频繁地修改bitmap
你每次改变一个bitmap的内容,你下次去画它时它会作为一个GPU纹理重新上载.
-
小心使用alpha(透明度)
当你用setAlpha(),AlphaAnimation,或ObjectAnimator把一个view设为透明,它将被呈现到一个离屏缓冲中,此时就需要双倍的填充速率.当在一个very大的view上应用透明度时,应考虑把view的layer类型设置为LAYER_TYPE_HARDWARE.