Android性能优化之UI优化篇

一、相关知识点

VSYNC信号、Overdraw(过度绘制)

二、UI优化工具

1、Hierarchy Viewer

性能优化工具知识梳理(4) – Hierarchy Viewer

2、真机调试-调试GPU过度绘制

性能优化工具知识梳理(3) – 调试GPU过度绘制 & GPU呈现模式分析

比如三星手机的开发者选项中有调试GPU过度渲染工具。

颜色:蓝色<浅绿<浅红<深红 

分别代表:绘制一次<绘制两次<绘制三次<绘制四次及以上

3、Lint检查

性能优化工具知识梳理(8) – Lint

三、UI优化技巧

1、减少布局层级

相信很多人为了图布局方便各种布局嵌套,结果导致层级过深。提升布局性能的关键点是尽量保持布局层级的扁平化,避免出现重复的嵌套布局。

这里介绍几种方法避免,有些用起来很简单有些确实会比较繁琐。

1)利用View的特殊属性

a、利用TextView滑动属性来避免嵌套一层ScrollView。

    在xml中添加TextView的属性:android:scrollbars =”vertical”

    在代码中添加:textView.setMovementMethod(newScrollingMovementMethod())

b、利用TextView的drawableStart属性等设置icon

原始代码👇

《Android性能优化之UI优化篇》 图3.1.a

如上图3.1.a 这个界面只是为了做一个title返回用了四层布局。

修改后代码👇

《Android性能优化之UI优化篇》 图3.1.b

如上图3.1.b 这是通过TextView的drawableStart属性设置返回箭头icon,将原本四层布局减少到二层布局,效果一样。

使用接下来讲的方法,上面的代码还可以优化哦。

2)使用merge标签

如果布局的最外层和它所在的父容器控件相同那么就使用merge吧。

比如,自定义一个控件继承LinearLayout,若该自定义控件需要填充布局,填充的布局第一层是LinearLayout的话,我们就可以使用merge标签。

再比如,activity的布局文件第一层是FrameLayout的话,我们就可以使用merge标签。

《Android性能优化之UI优化篇》 图3.2.a

图3.1.b的代码修改成图3.2.a 将原本二层布局减少到一层布局,效果一样。

3)使用ViewStub标签延迟加载布局

a、ViewStub特点

ViewStub是一个不可见的,大小为0的视图,可以在运行过程中延时加载布局资源。ViewStub被设置成可见或者它的inflate()方法被调用之后填充的布局才会加载。

可以这么理解这句话:Android在绘制界面时会略过ViewStub,直到ViewStub被填充,界面再次绘制才会显现填充后的视图。

b、何时使用ViewStub?

b.1、 某个布局的显现需要条件。

比如应用第一次使用某功能、记录数据为空等等不常出现的UI需要提醒用户感知。

b.2、控制整个布局的显示与隐藏。

b.3、不需要第一时间出现在用户视野的布局。

4)约束布局ConstraintLayout


《Android性能优化之UI优化篇》 图3.4.a

如上图3.4.a 实现 关于App说明的内容,包括app图标、版本号、检查更新、新手引导、产品介绍等内容。该布局用了二层层级结构完成,如果我们改用ConstraintLayout会发什么事呢?

《Android性能优化之UI优化篇》 图3.4.b

答案是我们完全不用嵌套,一层结构即可完成布局,当然这要求我们对ConstraintLayout使用熟练。有关ConstraintLayout的用法可以参考https://www.jianshu.com/p/17ec9bd6ca8a

5)自定义View减少布局层级,优化onDraw方法

一般来说,有两点需要注意:

避免在其中进行对象的分配

使用Canvas的ClipRect方法避免过度绘制

2、减少过度绘制

什么是过度绘制,我的理解是GPU在同一区域进行了不必要的多次绘制。

项目中的过度绘制和布局层级过多一样“出色”。大部分是设置多余的背景色和不可见区域的绘制导致。

1)什么时候设置View背景以及设置在哪里?去掉不必要的背景

比如说你想让一个Activity页面呈现白色背景,你可能会这么写

《Android性能优化之UI优化篇》

直接在XML根View写上android:background=”@color/color_ffffff”,这样会导致过度绘制,为什么呢?

我们知道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方法是一个空实现,因此它只占位置,而不去渲染,使用它来进行占位填充比其它控件更加高效

我的GitHub

    原文作者:Li_Pin
    原文地址: https://www.jianshu.com/p/79e7705e373a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞