设计者对 UI 的效果要求愈来愈高,致使的直接结果是界面复杂度愈来愈高。界面越复杂,加载速度越慢,界面加载越慢,用户体验越差,用户体验越差,App 被卸载的几率就越大。所以,布局布局优化势在必行。html
Android 设备 UI 刷新主要涉及三个模块,分别是:python
CPU 用于计算数据,GPU 用于将图像数据进行栅格化处理并把处理好的数据存入 BUFFER,DISPLAY 从 BUFFER 中取数据并将图像数据展现出来:android
但这是理想状态,实际状况是:
DISPLAY 的刷新率和 GPU 的帧率并不能总保持一致,也就是说,GPU 存数据的时间和 DISPLAY 取数据的时间是不一致的。针对此,Android 引入了 Vsync(垂直同步)。性能优化
Vsync 主要是为了让 DISPLAY 的刷新率和 GPU 的帧率保持一致,只有收到 Vsync 信号的时候,CPU 才开始计算数据、GPU 才开始处理数据、DISPLAY 才开始展现数据。bash
人眼与大脑之间的协做没法感知超过 60 fps(Frame per Second)的画面更新,也就是当画面的更新频率超过 60HZ 以后,人眼已经看不出它和更新频率为 60HZ 的界面之间的区别了。app
1s = 1000ms
1000ms / 60 = 16.7 mside
所以,每 16.7 ms 刷新一次界面,用户就会感受很流畅。也正是所以,View 的 measure、layout、draw 必须在 16ms 内完成,不然用户将感受到卡顿。模块化
一秒内刷新屏幕的次数,如 60 HZ,多数状况下,屏幕刷新率越高越好。不过,若是画面的内容还未生成,刷新再快也没有什么卵用,不是吗?工具
GPU 在一秒内操做界面的帧数,如 30 fps,60 fps。布局
我用过的排查布局性能的工具备三种:
Android Studio 自带的用于检测布局文件嵌套层级的工具,能够在开发阶段方便的检测布局文件的嵌套层级。
打开的方法:
Tools → Layout Inspector → 选择要测试的 App
复制代码
在左边「View Tree」栏能够查看当前界面的嵌套层级,「View Tree」右边对应的就是当前界面的显示的效果,当点击「View Tree」中不一样的控件时,「View Tree」右边界面中的相应控件会被蓝色框框起来。
打开手机自带「过分绘制检测工具」方法:
设置 → 开发者选项 → 显示过分绘制区域
复制代码
打开「显示过分绘制区域」以后,界面就会根据当前像素被绘制了几回显示不一样的颜色:
各类颜色表明的意思:
颜色 | 过分重绘次数 |
---|---|
蓝色 | 1 |
绿色 | 2 |
粉色 | 3 |
红色 | 4+ |
举个例子,建立一个下面这样的「我的中心」,这是经手机自带的「过分绘制检测工具」检测的结果:
systrace 搭配 traceview 定义布局性能问题,经过 systrace 定位大方向,经过 traceview 解析具体细节。
具体步骤:
在 Android 中,并非全部的 ViewGroup 性能都同样,若是非要排个序的话,那大概会是下面这个样子:
ConstraintLayout > FrameLayout > LinearLayout > RelativeLayout
复制代码
上面的排序是针对「四种容器」均能实现的效果而说的,也就是说,假设有某一场景,以上四种布局都可实现,ConstraintLayout 性能是最好的。
ConstraintLayout 能在不经任何嵌套的状况下,建立复杂的布局,所以,在从此的开发中应多使用它。另外,当 LinearLayout 和 RelativeLayout 均能在无需嵌套的状况下实现某效果时,用 LinearLayout,由于 RelativeLayout 测量时须要测量两次(当 LinearLayout 的子 View 有 layout_weight 属性时,也须要测两次)。
在实现某个布局时,若是有可能不用任何嵌套层级,那就不要用任何嵌套层级实现该界面。
举个例子,有一个登陆界面,分别用两种方式实现:
//1. 第一种实现方式:嵌套的 LinearLayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="@dimen/item_height"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/item_height"
android:paddingBottom="@dimen/padding_medium">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="@dimen/padding_ninety_six"
android:layout_height="@dimen/padding_ninety_six"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/padding_ninety_six"
android:scaleType="centerCrop"
android:src="@drawable/bird_woodpecker"
app:civ_border_color="@color/grey_800"
app:civ_border_width="@dimen/padding_micro_x" />
<LinearLayout
android:id="@+id/login_username_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_large"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_username" />
<EditText
android:id="@+id/login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:background="@null"
android:hint="@string/login_username_hint"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_medium"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_password" />
<EditText
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:background="@null"
android:hint="@string/login_password_hint"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_marginRight="@dimen/padding_medium"
android:background="@drawable/ripple_login"
android:clickable="true"
android:elevation="@dimen/divider_height"
android:gravity="center"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/login"
android:textColor="@color/grey_700"
android:textSize="@dimen/font_small"
android:textStyle="bold" />
</LinearLayout>
复制代码
经过 LayoutInspector 检测该界面的嵌套层级:
//2. 第二种实现方式:RelativeLayout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/item_height"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/item_height"
android:paddingBottom="@dimen/padding_medium">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="@dimen/padding_ninety_six"
android:layout_height="@dimen/padding_ninety_six"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/padding_ninety_six"
android:scaleType="centerCrop"
android:src="@drawable/bird_woodpecker"
app:civ_border_color="@color/grey_800"
app:civ_border_width="@dimen/padding_micro_x" />
<TextView
android:id="@+id/login_username_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/avatar"
android:layout_marginTop="@dimen/padding_large"
android:text="@string/login_username" />
<EditText
android:id="@+id/login_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/avatar"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_toRightOf="@id/login_username_label"
android:background="@null"
android:hint="@string/login_username_hint"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
<View
android:id="@+id/login_username_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_below="@id/login_username_label"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login_password_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/login_username_divider"
android:layout_marginTop="@dimen/padding_medium"
android:text="@string/login_password" />
<EditText
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/login_username_divider"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_medium"
android:layout_toRightOf="@id/login_password_label"
android:background="@null"
android:hint="@string/login_password_hint"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true"
android:textColor="@color/grey_700"
android:textCursorDrawable="@drawable/common_edit_text_cursor"
android:textSize="@dimen/font_micro" />
<View
android:id="@+id/login_password_divider"
android:layout_width="match_parent"
android:layout_height="@dimen/padding_micro_xx"
android:layout_below="@id/login_password_label"
android:layout_marginTop="@dimen/padding_small"
android:layout_marginBottom="@dimen/padding_small"
android:background="@color/grey_300" />
<TextView
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/login_password_divider"
android:layout_marginLeft="@dimen/padding_medium"
android:layout_marginTop="@dimen/padding_large"
android:layout_marginRight="@dimen/padding_medium"
android:background="@drawable/ripple_login"
android:clickable="true"
android:elevation="@dimen/divider_height"
android:gravity="center"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/login"
android:textColor="@color/grey_700"
android:textSize="@dimen/font_small"
android:textStyle="bold" />
</RelativeLayout>
复制代码
经过 LayoutInspector 检测该界面的嵌套层级:
在使用 标签引入其余布局文件时,不会产生多余的嵌套层级。由于布局层级少,因此绘制的工做量小,所以绘制速度更快、性能更高。
须要注意的是,最终 标签仍是要靠 或 标签来引用才能真正的「被使用」,另外, 标签自己是没有任何属性能够配置的, 标签用的是父 View 的属性。
举个例子:
//1. <merge> 标签
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/clear_cache"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/version_update"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/about"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/logout"
android:textSize="@dimen/font_micro" />
</merge>
//2. 引用 <merge> 标签
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/merge_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider_line"
android:dividerPadding="@dimen/padding_medium"
android:orientation="vertical"
android:showDividers="middle">
<LinearLayout
android:id="@+id/user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/item_height"
android:layout_marginBottom="@dimen/item_height"
android:gravity="center_horizontal"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/user_avatar"
android:layout_width="@dimen/padding_ninety_six"
android:layout_height="@dimen/padding_ninety_six"
android:scaleType="centerCrop"
android:src="@drawable/bird_woodpecker"
app:civ_border_color="@color/grey_800"
app:civ_border_width="@dimen/padding_micro_x" />
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_medium"
android:text="@string/test_list_item6"
android:textColor="@color/grey_700"
android:textSize="@dimen/font_large" />
</LinearLayout>
<include
android:id="@+id/marge_include"
layout="@layout/activity_main_merge_child"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
复制代码
标签中的内容:
引用 标签:
经过 LayoutInspector 检测该界面的嵌套层级:
由检测结果可知:
标签引入其余布局文件时,确实不会产生多余的嵌套层级。
在 Android 开发中,不只 Java 代码能够被复用,XML 布局文件的内容也能够被复用。在 Android 中,布局文件的复用,要经过 标签。
不少 App 中,用的都是自定义的 Title,像这种在多个地方都使用且内容相同的构件,彻底不必每次使用的时候都从新写一个,而是能够只写一个,在多个地方使用。
举个例子,建立一个通用的 Title 布局,经过 标签使用:
//1. Title Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey_700"
android:orientation="horizontal">
<ImageView
android:id="@+id/back"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:padding="@dimen/padding_medium_x"
android:src="@drawable/icon_back" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="@dimen/item_height"
android:layout_toLeftOf="@id/share"
android:layout_toRightOf="@id/back"
android:gravity="center"
android:text="@string/test_title"
android:textColor="@color/white"
android:textSize="@dimen/font_medium" />
<ImageView
android:id="@+id/share"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_alignParentRight="true"
android:padding="@dimen/padding_medium_x"
android:src="@drawable/icon_share" />
</RelativeLayout>
//2. 在 SecondActivity 中经过 <include> 标签引用 Title Layout
<?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="wrap_content"
android:background="@color/grey_700"
android:orientation="horizontal">
<include
layout="@layout/layout_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
复制代码
标签中的内容:
引用 标签:
经过 LayoutInspector 检测该界面的嵌套层级:
由检测结果可知:
标签引入其余布局文件时,会产生多余的嵌套层级。
须要注意的是,当在 标签中从新定义 标签所引用的布局文件属性时,将覆盖掉原布局文件中定义的属性。例如,原布局文件的 layout_width 和 layout_height 为 wrap_content,而 标签中定义的 layout_width 和 layout_height 为 match_parent,那最终显示的效果将是 match_parent。
使用 标签的好处除了复用 XML 布局文件以外,还有一个好处——将复杂的布局模块化,提升复用率,下降学习成本。
在 Android 中,默认状况下,全部的 View 都是须要测量(measure())、布局(layout())和绘制(draw())的,不管该 View 是否可见。
但有这样一种标签布局,只有当它可见的时候才会占用资源,它就是 ViewStub。所以,咱们能够借助它的这个特性对布局进行一些优化。
举个例子,在一个界面中请求数据时,当请求失败时展现一个错误提示界面,当请求成功时,展现请求到的信息:
//1. 错误提示界面
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/icon_error" />
//2. 引用「错误提示界面」的界面
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/user_info_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".third.ThirdActivity">
<ViewStub
android:id="@+id/error_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/layout_error" />
<include
android:id="@+id/user_info"
layout="@layout/activity_main_merge_parent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
//3. 在 Activity 中作相应的处理
public class ThirdActivity extends AppCompatActivity implements View.OnClickListener {
private boolean isInvalid = true;
private FrameLayout mContainer;
private LinearLayout mUserInfo;
private ViewStub mErrorPlaceHolder;
private View mErrorView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
initView();
}
private void initView(){
mContainer = findViewById(R.id.user_info_container);
mContainer.setOnClickListener(this);
mUserInfo = findViewById(R.id.user_info);
mErrorPlaceHolder = findViewById(R.id.error_placeholder);
}
@Override
public void onClick(View view) {
isInvalid = !isInvalid;
if(isInvalid){
mUserInfo.setVisibility(View.VISIBLE);
mContainer.setBackgroundColor(getResources().getColor(R.color.white));
if(mErrorView != null){
mErrorView.setVisibility(View.INVISIBLE);
}
Toast.makeText(this, getString(R.string.test_request_succeed), Toast.LENGTH_SHORT).show();
}else{
mUserInfo.setVisibility(View.INVISIBLE);
mContainer.setBackgroundColor(getResources().getColor(R.color.cyan_200));
if(mErrorView == null){
mErrorView = mErrorPlaceHolder.inflate();
mErrorView.setVisibility(View.VISIBLE);
}else{
mErrorView.setVisibility(View.VISIBLE);
}
Toast.makeText(this, getString(R.string.test_request_failed), Toast.LENGTH_SHORT).show();
}
}
}
复制代码
须要注意的是,默认状况下,ViewStub 是不可见的,也是不占用资源的,只有 inflate() 以后,ViewStub 才会占用资源,与此同时,ViewStub 自己也会被它指向的布局文件(ViewStub 中 layout 属性对应的布局文件)所替代。
ViewStub inflate() 以前,经过 LayoutInspector 检测该界面的嵌套层级:
由检测结果可知,此时 ViewStub 在 View Tree 中是虚线,也就表示它此时不占用资源。
ViewStub inflate() 以后,经过 LayoutInspector 检测该界面的嵌套层级:
由检测结果可知,此时 ViewStub 已经被它所指向的布局文件所替代。
//1. 定义分割线
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="@dimen/padding_micro_xx"
android:height="@dimen/padding_micro_xx" />
<solid android:color="@color/grey_200" />
</shape>
//2. 在 LinearLayout 中应用分割线
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider_line"
android:dividerPadding="@dimen/padding_medium"
android:orientation="vertical"
android:showDividers="middle"
tools:context=".four.FourActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/clear_cache"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/version_update"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/about"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/logout"
android:textSize="@dimen/font_micro" />
</LinearLayout>
复制代码
最终效果:
//1. TextView 同时显示文字和图片
<?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"
android:divider="@drawable/divider_line"
android:dividerPadding="@dimen/padding_medium"
android:orientation="vertical"
android:showDividers="middle">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_01"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_traffic"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_02"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_clothes"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_03"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_diet"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_04"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_communication"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_05"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_live"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_06"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_game"
android:textSize="@dimen/font_micro" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/icon_animal_07"
android:drawableRight="@drawable/icon_forward"
android:drawablePadding="@dimen/padding_medium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/padding_medium"
android:paddingTop="@dimen/padding_medium"
android:paddingRight="@dimen/padding_medium"
android:paddingBottom="@dimen/padding_medium"
android:text="@string/spending_others"
android:textSize="@dimen/font_micro" />
</LinearLayout>
复制代码
最终效果:
正如启动优化同样,布局优化也势在必行,由于,复杂的布局会影响其加载速度,而布局加载速度越慢,用户体验越差,用户体验越差,App 被卸载的几率就越大,而这不是开发者想看到的,也不是老板想看到的。
布局优化的总目标就一个——快速加载布局,布局优化的策略有:
我相信,在使用了上面这些策略以后,你开发的 App 的性能确定会有必定的提高,赶忙去试试吧~