使用这个模型你不能依赖一个View和脏区域有交集就会执行draw()方法。要确保Android系统记录了一个View的显示列表,你必须调用invalidate()方法,如果忘记了调用刷新,会使View即便是发生了改变后也会看起来相同,这是一个比较容易发现bug的方式。
使用显示列表的方式对动画的表现也是很有好处的,因为设置指定的属性值,比如透明度或者旋转,就不需要请求刷新目标View(这将自动执行)。这项优化也应用于有显示列表的Views(启用了硬件加速的View),例如,现在有一个LinearLayout包含了一个ListView和Button,listview在button的上面。这时候LinearLayout的显示列表如下所示:
假设你现在你想更新这个Listview的不透明度,在设置Listview的 setAlpha(0.5f) 属性之后,LinearLayout的显示列表应该包含如下:
这时候绘制Listview的复杂过程就会省略了,取而代之的是简单的更新了LinearLayout的显示列表。如果一个应用程序并没有启用硬件加速,Listview和它的父view的画的代码都会重新执行。
3.View的层
3.1层的分类
所有的Android版本都有能力对离屏缓冲进行渲染,或者是使用View的绘制缓冲,或者是使用Canvas.saveLayer()函数。离屏缓冲或者Layer能够有很多种应用,例如能使处理复杂view的动画效果或者应用一些合成效果都有更好地表现。例如你可以通过Canvas.saveLayer()的方式来对View做一个渐入渐出效果同时把它渲染到Layer中,然后再加上不透明效果合成后显示到屏幕上。
由Android 3.0开始你就能够通过方法对何时以及如何使用层有了更多的控制,这个API具有两个参数一个是你想使用的层类型,另外一个是可选参数Paint表明了Layer是如何被叠加的。你可以把Paint参数应用到颜色过滤上,特别是混合模式或者是对一个layer进行不透明效果。一个View可以使用如下的三种layer类型之一:
3.2 层的使用
使用层的类型取决于你的目的:
1. 性能:使用硬件层来渲染一个View成为硬件纹理。一旦一个View被渲染为一个层,它的绘制代码将不会得到执行,直到你调用了invalidate()函数。对于一些动画,比如透明动画可以直接应用到一个层上,这是GPU最有效率的使用方式。
2.显示效果:使用硬件或者软件层和Paint来对一个View进行特殊的视觉处理,例如你可以对一个View通过使用ColorMatrixColorFilter来实现黑白效果。
3.兼容性:使用软件层类型会强制使一个view在软件中被渲染。如果一个view是硬件加速的话(比如你设置整个应用程序是硬件加速的话),同时有渲染的问题,这是一种很简单的方式来限制硬件绘制流程。
3.3 View的层和动画的关系
当你的应用程序已经使用了硬件加速的话,硬件层能够带来更为快速和更为平滑的动画效果。当对一个复杂的View进行动画操作时,因为要进行很多的画操作,所以并不可能总是能达到60帧每秒。在这种情况下可以通过硬件层来渲染为硬件纹理来提高性能。硬件纹理操作可以用作对一个view进行动画操作,当进行动画的时候可以减少对View自身频繁的重绘。除非你改变这个view的属性(调用invalidate()方法)或者你手动的调用invalidate()。如果在你的应用中运行一个动画,但是并没有得到你想要的平滑效果,可以考虑为你要动画的view开启硬件层。
当一个View通过硬件层返回时,当所有的层叠加后最终的画面显示在屏幕时,View一些属性会被同时被处理。设置这些属性是十分有效率的,因为他们不需要View去invalidate和重绘。如下的属性将影响层的叠加,设置这些属性将会使View自动请求刷新,而且不需要对View进行重绘。
· pivotX, pivotY: 指定它进行变形的原点位置
这些属性是通过ObjectAnimator对象对一个view进行动画操作时所使用的,如果你想访问这些属性,直接调用这些属性的setter或者getter方法,例如想改变View的alpha则直接调用setAlpha()。如下的代码片段显示了一个View通过Y轴进行3D旋转。
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();
因为硬件层会消耗视频的内存,强烈的推荐你在作动画的时候启用他们,当动画完成了之后禁用他们,你可以通过动画监听来完成这些。代码如下:
View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator= ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
animator.start();
4.提示和技巧
切换到硬件加速2D图形可以立即增强表现,但是你还是需要通过如下的建议来设计你的应用程序来更有效率的使用GPU。
4.1 减少你程序中使用View的数量
你系统中画的view的数量越多,你的程序就会越慢,在软件绘制的流程也是一样的,减少view的数量是优化你UI的一个最简单的方法。
4.2 避免过多绘制
不要过多的叠加层,当一个View被其他view完全遮挡住了的话,最好把被遮挡的view移除掉。如果你需要绘制不同的层做一个叠加效果的话,考虑把这些层合并为一个层。就现在的硬件来看,有一个好的经验就是动画的每帧不要绘制多余屏幕像素2.5倍的像素数量(bimap中的透明像素也计算在内)。
4.3 不要在绘制的方法中创建绘制对象
一个常见的错误就是当绘制方法被调用的时候,每次都要创建一个新的Paint或者Path。这将迫使垃圾回收器过于频繁的运行,这将对缓冲和硬件的绘制造成影响。
4.4 不要过于频繁的修改形状
以复杂的shapes,path和旋转为例,这些绘制都会用到纹理的遮罩。每当你创建或者修改一个path,硬件渲染过程都会创建一个新的遮罩,这耗费的代价是相当大的。
4.5 不要过于频繁的修改bitmap
每当修改一次bitmap的内容,当你下次再绘制它的时候都会以GPU的纹理形式上传一次。
4.6 要小心使用alpha通道