图形重绘Overdraw

在Android界面上,图形的绘制与真实的绘画是非常类似的,例如绘画中先填充背景色,再在背景色上进行绘画。例如在一个LinearLayout中绘制蓝色背景色,再在LinearLayout上半部分画满绿色的Button,这样上面一半LinearLayout的背景色就被浪费了,成为了Overdraw的图形。因为Android系统在绘制这些View的时候,其实并不知道哪些View是可见的、不被遮挡的,它只能根据设置全部绘制。然后大量重复的绘制会导致系统做很多无用功,延长了绘制的时间,降低了绘制的效率。

图形重绘Overdraw - 图1在Android 4.4里面,Google采用了Overdraw Avoidance技术,系统可以自带检测Overdraw,避免过多的Overdraw。但是这个功能非常有限,只有当两个View全部遮挡时,才会触发。因此,开发者在减少Overdraw的时候,大部分时间还是得靠自己。

Overdraw与布局冗余检测实例

界面重绘和布局冗余应该是UI性能优化中最容易被修改的部分,下面笔者以一个实例来讲解一下如何进行检测和分析。示例所使用的布局代码如下所示。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:paddingBottom="@dimen/activity_vertical_margin"
  8. android:paddingLeft="@dimen/activity_horizontal_margin"
  9. android:paddingRight="@dimen/activity_horizontal_margin"
  10. android:paddingTop="@dimen/activity_vertical_margin"
  11. tools:context="com.xys.preferencetest.MainActivity">
  12.  
  13. <LinearLayout
  14. android:layout_width="match_parent"
  15. android:layout_height="match_parent">
  16.  
  17. <TextView
  18. android:layout_width="match_parent"
  19. android:layout_height="match_parent"
  20. android:background="#bebebe"
  21. android:gravity="bottom"
  22. android:text="First TextView"/>
  23. </LinearLayout>
  24.  
  25. <LinearLayout
  26. android:layout_width="match_parent"
  27. android:layout_height="400dp">
  28.  
  29. <TextView
  30. android:layout_width="match_parent"
  31. android:layout_height="400dp"
  32. android:background="#7c7575"
  33. android:gravity="bottom"
  34. android:text="Second TextView"/>
  35. </LinearLayout>
  36.  
  37. <LinearLayout
  38. android:layout_width="match_parent"
  39. android:layout_height="300dp">
  40.  
  41. <TextView
  42. android:layout_width="match_parent"
  43. android:layout_height="300dp"
  44. android:background="#bebebe"
  45. android:gravity="bottom"
  46. android:text="Third TextView"/>
  47. </LinearLayout>
  48.  
  49. <LinearLayout
  50. android:layout_width="match_parent"
  51. android:layout_height="200dp">
  52.  
  53. <TextView
  54. android:layout_width="match_parent"
  55. android:layout_height="200dp"
  56. android:background="#7c7575"
  57. android:gravity="bottom"
  58. android:text="Fourth TextView"/>
  59. </LinearLayout>
  60.  
  61. </RelativeLayout>

这个布局所显示的效果如图6.4所示。

四个TextView从大到小,依次叠加摆放。按照步骤,先打开Debug GPU Overdraw工具,查看重绘界面,显示如图6.5所示。

图形重绘Overdraw - 图2 图6.4 重叠布局

图形重绘Overdraw - 图3 图6.5 检测重叠布局

由于这四个TextView是叠加摆放的,所以上面的TextView发生了重绘问题。通过这个工具可以很快地找到具体的重绘点,通常情况下可以通过下面所列举的方法进行修改。

  • 控件重叠摆放:改善布局方式,避免重叠。
  • 控件与主背景颜色相同:可移除控件背景(移除不必要的背景)。
  • 自定义View重叠:在绘制时,使用clipRect属性减少重绘区域。

接下来,再使用Hierarchy Viewer工具查看布局,显示如图6.6所示。

图形重绘Overdraw - 图4 图6.6 Hierarchy Viewer

在ContentView这样一个根布局中,出现了四个线型的View树,也就是上面的四个无效的线型布局。通过这个工具可以快速找到冗余的布局,从而去掉冗余布局来提高性能。同时还需要尽可能降低UI的层级,将UI树扁平化,提高绘制的效率,而页面计算、布局、绘制的时间都可以在图中找出来。