設計者對 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 的性能確定會有必定的提高,趕忙去試試吧~