查找Overdraw
Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在重叠的UI布局中,如果不可见的UI也在做绘制的操作或者后一个控件将前一个控件遮挡,会导致某些像素区域被绘制了多次,从而增加了CPU,GPU的压力。
按照以下步骤打开Show GPU Overrdraw的选项:设置 -> 开发者选项 -> 调试GPU过度绘制 -> 显示GPU过度绘制
打开调试GPU过度绘制开关之后会发现屏幕上有各种颜色,不同的颜色代表过度绘制的程度,具体如下表:
蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,?x分别表示同一像素上同一帧的时间内被绘制了?次,1x就表示一次最理想情况,4x表示4次最差的情况,我们要做的就是尽量减少3x,4x的情况出现。
Overdraw 的处理
1、减少布局层级
使用工具Layout Inspector
按以下步骤操作:
- 在连接的设备或模拟器上运行应用。
- 点击 Tools > Android > Layout Inspector。
- 在出现的 Choose Process 对话框中,选择您想要检查的应用进程,然后点击 OK。
提升布局性能的关键点是尽量保持布局层级的扁平化,避免出现重复的嵌套布局。
这里介绍几种方法避免,有些用起来很简单有些确实会比较繁琐。
1)利用View的特殊属性
- 利用TextView滑动属性来避免嵌套一层ScrollView。
在xml中添加TextView的属性:android:scrollbars ="vertical";
在代码中添加:textView.setMovementMethod(newScrollingMovementMethod())。
- 利用TextView的drawableStart属性等设置icon
2)使用merge标签
如果布局的最外层和它所在的父容器控件相同那么就使用merge,这样可以将原本二层布局减少到一层且效果一样。
- 自定义一个控件继承LinearLayout,若该自定义控件需要填充布局,填充的布局第一层是LinearLayout的话,我们就可以使用merge标签。
- activity的布局文件第一层是FrameLayout的话,我们就可以使用merge标签。
3)使用ViewStub标签延迟加载布局
ViewStub是一个宽高都为0、不可见的视图,可以在运行过程中延时加载布局资源。ViewStub被设置成可见或者它的inflate()方法被调用之后填充的布局才会加载。
何时使用ViewStub?
- 某个布局的显现需要条件。比如应用第一次使用某功能、记录数据为空等等不常出现的UI需要提醒用户感知。
- 控制整个布局的显示与隐藏。
- 不需要第一时间出现在用户视野的布局。
4)约束布局ConstraintLayout
使用ConstraintLayout完全不用嵌套,一层结构即可完成布局,当然这要求我们对ConstraintLayout使用熟练。可以参考ConstraintLayout的用法。
5)自定义View减少布局层级,优化onDraw方法
一般来说,有两点需要注意:
避免在循环中进行对象的分配
使用Canvas的ClipRect方法避免过度绘制
2、减少过度绘制
GPU在同一区域进行了不必要的多次绘制。
1)去掉不必要的背景
比如想让一个Activity页面呈现白色背景,你可能会直接在XML根View写上android:background="@color/color_ffffff",这样会导致过度绘制,why?
因为Activity的xml布局最终会添加到一个叫“content”的FlameLayout中,Android系统本身对这个FlameLayout设置背景颜色,如果我们在加入这个FlameLayout容器的XML根View中再设置背景颜色,就相当于GPU做了两次绘制,同一区域绘制了2次背景色,导致过度绘制。
解决:在activity setContentView()前findViewById(android.R.id.content).setBackgroundResource(R.color.color_ffffff)。
2)不可见区域不要绘制,ClipRect & QuickReject
某些UI在特定操作后出现,比如刷新时背景出现,刷新后背景隐藏。
对于这种情况我们可以监听刷新操作,获取下拉距离,利用canvas的clipRect()方法只绘制规定矩形内的区域。
除了clipRect方法之外,我们还可以使用canvas的quickReject()来判断是否和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。
3)去掉无用的WindowBackgroud,移除Window默认的Background
当使用某些主题时,系统有可能在DecorView中给我们加上一个背景,但是有时候它是无用的
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_overdraw);
getWindow().setBackgroundDrawable(null);
}
4)按需显示占位背景图片
3、布局复用
1)使用 <include> 标签
当我们的布局中有多个相同的布局时,可以使用include标签来进行布局的复用
4、减少布局中的View
a、使用 SpannableStringBuilder替换多个
多种不同大小、颜色或者图文混排需要显示时,我们往往会利用多个TextView来进行组合,但是某些效果通过一个TextView就可以实现。
Android中各种Span的用法
b、使用LinearLayout自带的divider属性实现分割线,而不是在布局中手动添加一个额外的View作为分割线
与分割线相关的属性主要有:
- divider:传入分割线的drawable,可以是一个图片,也可以是自己通过xml实现的drawable。
- showDividers:分割线显示的位置,beginning/middle/end,分割对应头部、中间、尾部。
- dividerPadding:分割线距离两边的间距。
c、使用Space控件进行合理的占位
Space控件位于android.support.v4.widget包中,与一般控件不同,它的draw方法是一个空实现,因此它只占位置,而不去渲染,使用它来进行占位填充比其它控件更加高效