性能优化(1.2)-布局优化(扁平化,Merge的使用,ViewStub的使用)

主目录见:Android高级进阶知识(这是总目录索引)
 今天之所以讲这一篇主要是为了下一篇[APP启动速度优化实例解析]做铺垫的,我们都知道我们解决UI卡顿问题中主要就是:

  1. 优化CPU的计算时间或者不必要的布局导致测量布局时间变长;
  2. 优化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>

这里只是简单布局了下,大家凑合着看哈,效果图如下:

《性能优化(1.2)-布局优化(扁平化,Merge的使用,ViewStub的使用)》 顶部栏

那么如果我们的项目中会使用多次这个顶部栏的话(当然增加顶部栏不会每个页面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来查看我们刚才布局的层级:

《性能优化(1.2)-布局优化(扁平化,Merge的使用,ViewStub的使用)》 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一起联合使用。

总结:今天我们讲了这三个标签,应该说是开发过程中用的比较频繁的,希望大家如果又遇到布局优化问题能想到用这个来解决,当然,今天只是讲了一小部分的内容,优化的内容还是很多的,希望我们一起努力。

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