布局优化方案: 1. 避免过度绘制 2. 减少布局层级 3. 提高加载速度 4. 复用布局

一 避免过度绘制

什么是过度绘制?

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次的UI结构里面,如果不可见的UI也在做绘制的操作,这就会导致某些像素区域被绘制了多次。这就浪费大量的CPU以及GPU资源.当设计上追求更华丽的视觉效果的时候,我们就容易陷入采用越来越多的层叠组件来实现这种视觉效果的怪圈。这很容易导致大量的性能问题,为了获得最佳的性能,我们必须尽量减少Overdraw的情况发生.

如何检查绘制层数?

我们可以通过手机设置里面的开发者选项,打开Show GPU Overdraw(显示GPU过渡渲染)的选项,可以观察UI上的Overdraw情况。

安卓overlay默认不生效 android overlay详解_过度绘制

蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。 Overdraw有时候是因为你的UI布局存在大量重叠的部分,还有的时候是因为非必须的重叠背景。例如某个Activity有一个背景,然后里面的Layout又有自己的背景,同时子View又分别有自己的背景。仅仅是通过移除非必须的背景图片,这就能够减少大量的红色Overdraw区域,增加蓝色区域的占比。这一措施能够显著提升程序性能。

实践解决过度绘制

安卓overlay默认不生效 android overlay详解_过度绘制_02

上图是一个Fragment页面的布局,xml如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@color/bg"
              android:orientation="vertical">

    <LinearLayout
        android:id="@+id/lin_photo"
        android:layout_width="match_parent"
        android:layout_height="88px"
        android:background="@drawable/white_ripple"
        android:orientation="horizontal"
        android:paddingLeft="34px"
        android:paddingRight="34px">

        <TextView
            android:id="@+id/tv_user_head_icon"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:text="@string/setting_head"
            android:textColor="@color/black_111111"
            android:textSize="28px"/>

        <ImageView
            android:id="@+id/iv_user_head_icon"
            android:layout_width="28dp"
            android:layout_height="28dp"
            android:layout_gravity="center_vertical"
            android:background="@drawable/head_icon"
            android:scaleType="centerCrop"/>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="12px"
            android:background="@drawable/enter_arrow"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lin_name"
        android:layout_width="match_parent"
        android:layout_height="88px"
        android:background="@drawable/white_ripple"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="34px"
        android:paddingRight="34px">

        <TextView
            android:id="@+id/tv_user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="4dp"
            android:text="@string/setting_name"
            android:textColor="@color/black_111111"
            android:textSize="28px"/>

        <TextView
            android:id="@+id/tv_user_name_value"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:gravity="right"
            android:lines="1"
            android:textColor="@color/black_111111"
            android:textSize="28px"/>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="12px"
            android:background="@drawable/enter_arrow"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/lin_change_password"
        android:layout_width="match_parent"
        android:layout_height="88px"
        android:background="@drawable/white_ripple"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_marginTop="20px"
        android:paddingLeft="34px"
        android:paddingRight="34px">

        <TextView
            android:id="@+id/tv_modify_pwd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginRight="4dp"
            android:layout_weight="1"
            android:text="@string/change_password"
            android:textColor="@color/black_111111"
            android:textSize="28px"/>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="4dp"
            android:background="@drawable/enter_arrow"/>
    </LinearLayout>

    <TextView
        android:id="@+id/tv_out_login"
        android:layout_width="match_parent"
        android:layout_height="88px"
        android:background="@drawable/white_ripple"
        android:gravity="center"
        android:text="@string/out_login"
        android:layout_marginTop="20px"
        android:textColor="@color/black_111111"
        android:textSize="28px"/>

</LinearLayout>

看起来并没有多余的背景绘制.怎么就红了呢! 下面来分析解决:

(1), 去除默认背景

Android自带的一些主题theme,往往有一个默认背景,由DecorView持有.这个背景对我们来说大多情况是无用的,因此可以移除,代码如下:

getActivity().getWindow().setBackgroundDrawable(null);

如果你的APP设计背景是统一的Color的话,也不妨在style里的全局主题theme上添上:

<item name="android:windowBackground">@color/bg</item>

这样就不用重复在每个页面的XML里添加背景!

(2), 去除重复根背景

因为在FragmentActivity布局中已经设置了背景,所以Fragment的布局没必要在重复设置背景色.移除根布局的代码:

android:background="@color/bg"

注意:在adapter中,同样适用.例如ListView设置了背景,在item中就不用重复设置.

(3), 去除占位图

占位图只有在没有获取到图片时才显示,所以可以按需显示.在xml里移除代码:

android:background="@drawable/head_icon"

优化后效果:

安卓overlay默认不生效 android overlay详解_性能优化_03

参考文档:

http://hukai.me/android-performance-patterns/

下文接: Android性能优化之布局篇(二)