主目录见:Android高级进阶知识(这是总目录索引)
今天之所以讲这一篇主要是为了下一篇[APP启动速度优化实例解析]做铺垫的,我们都知道我们解决UI卡顿问题中主要就是:
- 优化CPU的计算时间或者不必要的布局导致测量布局时间变长;
- 优化GPU的过度绘制,主要方法可以在手机打开GPU检测或者在Android studio中的Hierarchy Viewer可以查看层级。
针对这两种情况一般会有下面的因素或者处理方式可以优化:
- 在拼接字符串时候尽量使用StringBuilder避免大量的GC导致卡顿;
- 避免在主线程做大量的计算任务,比如递归的操作导致CPU时间占用长导致卡顿;
- 去掉window的背景,因为DecorView默认会有一个纯色的背景,在我们布局设置了背景的话,那么这个背景对我们来说是多余的;
- 去掉不必要的背景,因为在我们布局有嵌套的情况下,如果都设置了背景的话有可能存在不必要的背景导致重绘;
- 利用clipRect的方式来减少绘制层数,一个典型的例子就是扑克牌重叠导致重绘;
- 利用include,merge,ViewStub等标签来减少嵌套层数。
今天我们就是主要来讲的最后一种方法的使用,这个方式应该说能很有效解决过渡绘制的问题。
一.目标
我们今天的目标也是很简单的,就是看这几个标签是怎么使用的,然后能在实际应用中使用到,所以今天目标:
1.学会include,merge,ViewStub这三个标签怎么使用;
2.明白什么情况下使用哪个标签以及用这三个标签来优化。
二.标签使用
1.<include/>重用布局
首先我们第一个来讲讲<include/>的使用,若几个布局界面存在较多的共同模块,可以进行代码块的重用,编写进入一个共同的布局里面,然后在多个布局文件中使用include标签进行引入。这里我们举个例子比如顶部栏:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp">
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/back_arrow"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="标题"
android:textColor="@color/colorPrimary"
android:textSize="20sp"
android:gravity="center"/>
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/btn_search"
android:layout_gravity="center"/>
</LinearLayout>
这里只是简单布局了下,大家凑合着看哈,效果图如下:
顶部栏那么如果我们的项目中会使用多次这个顶部栏的话(当然增加顶部栏不会每个页面include),那么我们这时候就可以用include:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/layout"/>
</LinearLayout>
这样我们就可以把公共的布局抽出来当做独立的部分了,这个标签应该来说用的还是比较多的。
2.<merge/>减少布局层数
我们知道我们布局解析的时候是一层一层递归调用rinflate,然后再回归讲view添加到父视图中去,最后整个视图才创建完毕。如果嵌套层数太多的话,就会导致这个解析的过程任务量变大从而导致解析速度变慢,这样的话我们可以用<merge/>标签来进行优化。我们首先来用androidstudio中的Hierarchy Viewer来查看我们刚才布局的层级:
Hierarchy Viewer查看布局可以看到我们布局是从最上面的contentFrameLayout往下的,为什么呢?因为我们看setContentView源码的时候我们知道,我们的布局是在id为content的
FrameLayout
下面,如果不知道可以参考setContentView源码分析这篇文章,然后我们看到往下的话还有两层的LinearLayout布局,很明显,有一层LinearLayout是没有用的。所以我们来优化一下:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/layout"/>
</merge>
我们看到,这样的话就少了一层LinearLayout,也可以把里面的LinearLayout去掉,但是这样的布局就要又外层的布局来决定了:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp">
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/back_arrow"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="标题"
android:textColor="@color/colorPrimary"
android:textSize="20sp"
android:gravity="center"/>
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/btn_search"
android:layout_gravity="center"/>
</merge>
那么我们外层的布局就要改成:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
>
<include layout="@layout/layout"/>
</LinearLayout>
好啦,到这里我们这个标签已经讲完了,这个标签的功能主要就是为了减少层数的。
3.<ViewStub/>延迟加载布局
其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才 会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。例如我们通过一个 ViewStub来惰性加载一个消息流的评论列表,因为一个帖子可能并没有评论,此时我可以不加载这个评论的ListView,只有当有评论时我才把它加 载出来,这样就去除了加载ListView带来的资源消耗以及延时:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/layout"/>
<ViewStub
android:inflatedId="@+id/network_error_id"
android:layout="@layout/network_error"
android:id="@+id/network_error"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
这里的inflatedId指的是layout/network_error的跟id,android:layout指的是布局network_error的布局。然后我们要让他显示出来:
if (null == netWorkError){
ViewStub netWorkErrorStub = (ViewStub)findViewById(R.id.network_error);
netWorkError = netWorkErrorStub.inflate();
}else{
netWorkError.setVisibility(View.VISIBLE);
}
ViewStub标签和GONE都是会使一个视图不可见,但是设置为GONE的话在渲染的时候还是会被添加到视图树里面,而ViewStub只有在inflate之后才会被添加到视图树上面,所以减少了一次性渲染的压力。
注意事项
- 判断是否已经加载过, 如果通过setVisibility来加载,那么通过判断可见性即可;如果通过inflate()来加载是不可以通过判断可见性来处理的,所以需要判断加载的视图是否为空来判断。
- findViewById的问题,注意ViewStub中是否设置了inflatedId,如果设置了则需要通过inflatedId来查找目标布局的根元素。
- ViewStub不能与merge一起联合使用。
总结:今天我们讲了这三个标签,应该说是开发过程中用的比较频繁的,希望大家如果又遇到布局优化问题能想到用这个来解决,当然,今天只是讲了一小部分的内容,优化的内容还是很多的,希望我们一起努力。
网友评论