2026-10-30 20:27:06
View优化之Overdraw
一、什么是Overdraw?
Overdraw即过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次。理论上,一个像素每次只绘制一次是最优的,但由于重叠的布局,一些像素会被多次绘制。每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这些操作耗时超过16.67ms时,就会出现掉帧现象,即卡顿。因此,对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。
二、如何检测Overdraw?
Android提供了测量Overdraw的选项,在开发者选项-调试GPU过度绘制(Show GPU Overdraw)中,打开该选项就可以看到当前页面Overdraw的状态。该工具会使用三种不同的颜色绘制屏幕,来指示overdraw发生在哪里以及程度如何:

三、如何减少Overdraw?
合理选择控件容器
LinearLayout:易用,效率高,但表达能力有限,只能用于描述一个方向上连续排列的控件。
RelativeLayout:复杂,表达能力强,效率稍逊,但可以用于描述任意复杂度的界面。
在选择容器时,要根据实际情况来选择合适容器控件,在保证性能的同时,尽量避免overdraw。如果某一界面在使用LinearLayout并不会比RelativeLayout带来更多的控件数和控件层级时,LinearLayout则是首选。
去掉window的默认背景
当使用了Android自带的一些主题时,window会被默认添加一个纯色的背景。如果自定义布局时又添加了一张背景图或设置背景色,那么DecorView的background就会产生一次Overdraw。可以通过在onCreate()中setContentView()之后调用getWindow().setBackgroundDrawable(null);或在theme中添加android:windowbackground="null"来去掉window的背景。
去掉其他不必要的背景
有时候为了方便,会先给Layout设置一个整体的背景,再给子View设置背景,这会造成重叠。如果子View宽度为match_parent,完全覆盖了Layout的一部分,就可以通过分别设置背景来减少重绘。另外,如果采用的是selector的背景,将normal状态的color设置为“@android:color/transparent”,也可以解决问题。
ClipRect & QuickReject
为了解决Overdraw的问题,Android系统会通过避免绘制那些完全不可见的组件来尽量减少消耗。但对于复杂的自定义View(通常重写了onDraw方法),Android系统无法检测在onDraw里面具体会执行什么操作,因此无法避免Overdraw。可以通过canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制,其他区域会被忽视。同时,canvas.quickreject()也可以用来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。
ViewStub
ViewStub是一个轻量级的View,它看不见、不占布局位置、占用资源非常小。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化。当ViewStub被设置为可见时,或调用了ViewStub.inflate()时,ViewStub所指向的布局就会被Inflate和实例化。这样可以使用ViewStub来方便地在运行时决定是否显示某个布局。
Merge
Merge标签可以干掉一个view层级。有两种情况下可以使用Merge标签来做容器控件:一是子视图不需要指定任何针对父视图的布局属性;二是假如需要在LinearLayout里面嵌入一个布局(或视图),而这个布局(或视图)的根节点也是LinearLayout,这样就多了一层没有用的嵌套。使用Merge标签可以避免这样的问题。但需要注意的是,Merge只能作为XML布局的根标签使用。
善用draw9patch
给ImageView加一个边框时,通常会在ImageView后面设置一张背景图来露出边框。但这样做会导致两层drawable的重叠区域绘制两次,产生overdraw。优化方案是将背景drawable制作成draw9patch,并将和前景重叠的部分设置为透明。由于Android的2D渲染器会优化draw9patch中的透明区域,从而优化了这次overdraw。但需要注意的是,背景图片必须制作成draw9patch才行。
慎用Alpha
对View做Alpha转化需要先将View绘制出来,然后做Alpha转化,最后将转换后的效果绘制在界面上。这样做会导致对当前View绘制两遍,绘制效率会大打折扣。如果一定需要做Alpha转化,可以采用缓存的方式,通过setLayerType方式将当前界面缓存在GPU中,但用完要马上释放掉。
避免“OverDesign”
Overdraw产生的原因无外乎复杂的Layout层级、重叠的View、重叠的背景等。开发人员无节制的View堆砌,究其根本是产品无节制的需求设计。因此,在复杂的业务逻辑与简单易用的界面展现中需要做一个平衡,而不是一味的OverDesign。
通过以上方法,可以有效地减少Overdraw,提高应用的性能。