一.概况
上一篇中App性能优化之View优化(1)——UI问题的检测主要讲的是我们在写完代码之后,如何发现代码中的问题。从问题中提取经验,规范好自己之后的代码编写,本文就是讲如何规范的进行UI关联代码的书写。
二.主要的点
- View控件的选择和布局文件层级优化;
- Include,Merge,ViewStub等标签的使用;
- 自定义View中关键步骤中减少耗时操作;
- 非图片类型Drawable的使用;
三.View控件的选择和层级优化
3.1原则:
尽量较少层级;使用高效的VIew控件;
3.2选择View控件:
常用的控件有LinearLayout和RelativeLayout和ConstraintLayout和FrameLayout在他们中做出选择。
1.层级尽量减少:能用ConstraintLayout或者RelativeLayout来减少层级,不用LinearLayout;(ConstraintLayout较RelativeLayout有更高的效率推荐使用)
2.多层级时候,相同层级的用LinearLayout不用RelativeLayout;(前者在measure,layout的过程中有更好的效率,这里不展开说明,我们可以使用上一篇文章中Hierarchy View工具进行验证
,查看measure,layout,draw的时间,有兴趣可以验证一下,这里不赘述)
3.作为容器控件的时候用FrameLayout;(单个的,如装Fragment的容器)
3.3去掉其他不必要的背景
3.3.1去掉window的默认背景
当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。
我们在Application层级或者Activity层级,在theme中更改windowbackground属性为null。
android:windowbackground="null";
3.3.2去掉其他不必要的背景
有时候为了方便会先给Layout设置一个整体的背景,再给子View设置背景,这里也会造成重叠,如果子View宽度mach_parent,可以看到完全覆盖了Layout的一部分,这里就可以通过分别设置背景来减少重绘。或者子View没有要求背景,那么给父VIew添加了背景就可以了。
四.Include,Merge,ViewStub等;
4.1Include标签来进行布局复用
多个页面拥有相同的局部布局的时候,并且这部分布局改动的可能性较小或者不动,那么就用Include标签。
4.2用Merge标签去除多余层级
Merge意味着合并,在合适的场景使用Merge标签可以减少多余的层级。Merge标签一般和Include标签搭配使用,适合使用的场景:
- merge标签最好是来替代FrameLayout;
- 布局方向一致的LinearLayout,比如当前父布局LinearLayout的布局方向是垂直的,包含的子布局LinearLayout的布局方向也是垂直的,则可以用merge标签来替换子布局LinearLayout;
4.3使用ViewStub来提高加载速度
使用场景:
一个很常见的开发场景就是我们想要一个布局时,并不是所有的控件都需要显示出来,而是显示出一部分,对于这种情况,我们一般采用的方法就是使用View的GONE和INVISIBLE,但是这种方法效率不高,虽然是达到了隐藏的目的,但是仍在布局当中,系统仍然会解析它们,我们可以用ViewStub来解决这一问题。
ViewStub的优势
ViewStub是轻量级的View,不可见并且不占布局位置。当ViewStub调用inflate方法或者设置可见时,系统会加载ViewStub指定的布局,然后将这个布局添加到ViewStub中,在对ViewStub调用inflate方法或者设置可见之前,它是不占布局空间和系统资源的,它主要的目的就是为目标视图占用一个位置。因此,使用ViewStub可以提高界面初始化的性能,从而提高界面的加载速度。
使用
ViewStub的内容布局文件title_bar
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Title"/>
</RelativeLayout>
ViewStub所在的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<ViewStub
android:id="@+id/vs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/title_bar"/>
</LinearLayout>
使用代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewStub vs = findViewById(R.id.vs);
//vs.inflate();
vs.setVisibility(View.VISIBLE);
}
要注意的点
- ViewStub只能加载一次,加载后ViewStub对象会被置为空,这样当ViewStub引用的布局被加载后,就不能用ViewStub来控制引用的布局了。因此,如果一个控件需要不断的显示和隐藏,还是要使用View的Visibility属性。
- ViewStub不能嵌套Merge标签。
- 上面的inflate和setVisiblity方法都可以。
4.4clipRect的使用
我们可以通过canvas.clipRect()来 帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。这个API可以很好的帮助那些有多组重叠 组件的自定义View来控制显示的区域。同时clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。
4.5非图片Drawable的使用
使用场景:
在一些View的背景为纯色或者已知渐变色的时候我们尽量用非图片Drawable代替切图,这样带来的好处是可以减小Apk的大小。
使用方法:
- 作为View的纯色背景(使用shape);
- 作为点击变色Button的纯色背景(selecter);