Android性能優(yōu)化之布局優(yōu)化

? ? ? ?不合理的布局會使我們應用程序UI性能變慢,客戶體檢會比較差。今天分享一些layout布局文件中的一些技巧,希望對大家寫出高質量的布局文件能有一些幫助。

在開始之前先介紹一個能幫助我們優(yōu)化布局的一個工具。

? ? ? ?Hierarchy Viewer工具,提供了一個可視化界面顯示布局的層次結構、以及查看每個界面measure,layout,draw所耗費的時間。給我們優(yōu)化界面布局結構提供了一個很好的參考。

一、ViewGroup的選擇

? ? ? ?所有的View都需要放在ViewGroup里面的顯示。有的時候相同的布局LinearLayout、RelativeLayout,FrameLayout,TableLayout,AbsoluteLayout里面的某幾種能實現。這個時候咱們應該選用那個ViewGroup呢。

? ? ? ?ViewGroup的選擇有下面幾點

  • 同樣的布局,層級深度小的一般優(yōu)于層級深度高的。(當然這也不是絕對的)

  • 同樣的布局,在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。(RelativeLayout會讓子View調用兩次onMeasure,LinearLayout一般會調用子View一次onMeasure,但是在有weight的時候,也會調用子View兩次onMeasure)

  • RelativeLayout子View盡量使用padding代替margin。

? ? ? ?RelativeLayout會讓子View調用兩次onMeasure,LinearLayout一般會調用子View一次onMeasure,但是在有weight的時候,也會調用子View兩次onMeasure。

? ? ? ?下面我們通過Hierarchy Viewer工具來對比下實現相同的布局,LinearLayout和RelativeLayout 測量耗時的對比。我們用LinearLayout和RelativeLayout來一個非常簡單的布局上下兩個button的布局。看下耗時的對比。

LinearLayout 實現布局


線性布局.png

RelativeLayout 實現布局


相對布局.png

? ? ? ?界面的顯示時間,我們只需要關注Measure的時間(Layout和Draw時間相差不到我們認為是一樣的)。相同的界面,哪個Measure時間端,性能就更好。實現相同的效果,Hierarchy Viewer顯示LinearLayout Measure時間 0.019 ms<RelativeLayout Measure時間 0.026 ms。LinearLayout 性能更優(yōu)。

二、include(布局重用)

? ? ? ?使用include標簽,將另一個xml文件引入,作為布局的一部分。include的最大的作用是便于布局重用(比如我們所有的界面的標題欄都是一樣的)。我們舉一個簡單的例子來說明。

我們先寫一個layout_title.xml的xml文件用來做所有界面統一的標題。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="42dp"
        android:gravity="center"
        android:background="@color/colorPrimary"
        android:text="標題"
        android:textSize="18sp"
        android:textColor="@android:color/white"/>

</RelativeLayout>

Activity對應的布局文件

<?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:orientation="vertical">

    <include
        layout="@layout/layout_title"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="就一個布局"
        android:textColor="@android:color/holo_red_dark"/>

</LinearLayout>

三、merge

? ? ? ?merge標簽主要用于刪除多余的層級避免嵌套過多無用的布局,減少布局的深度。

? ? ? ?用一個非常簡單的布局來說明merge標簽的使用。就在界面上顯示一個TextView。

? ? ? ? 這里我們用Layout Inspector工具來替換Hierarchy Viewer 來查看頁面布局的層級結構。Layout Inspector工具是Android Studio 替代 Hierarchy Viewer 的新方案

ViewGroup是FrameLayout,然后再里面放TextView

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="就一個布局"
        android:textColor="@android:color/holo_red_dark"/>

</FrameLayout>
不使用merge的層級結構.png

merge標簽,然后再里面放TextView

<?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">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="就一個布局"
        android:textColor="@android:color/holo_red_dark"/>

</merge>
使用merge的層級結構.png

? ? ? ?前后對比,發(fā)現使用merge的時候層級確實少了一層(少了FrameLayout)。

3.1、merge使用場景

3.1.1、Activity layout文件的根視圖是FrameLayout的時候

? ? ? ?因為我們在Activity中setContentView()的content對應的就是一個FrameLayout。所以當我們布局文件的根視圖也是FrameLayout的時候完全可以使用merge標簽來減少一層FrameLayout結構。

3.1.2、merge標簽配合自定義組合布局ViewGroup的使用

? ? ? ?自定義組合控件ViewGroup的時候,可以使用merge來減少一層ViewGroup。比如自定義繼承LinearLayout的組合控件,建議讓自定義View的layout布局文件根節(jié)點設置成merge。

3.1.3、merge標簽配合include標簽使用

? ? ? ?include標簽使用的時候要注意merge標簽的使用??赡苣承┣闆r下merge標簽可以派上用場。接下來我們來改造一下文章前面include標簽的實例。layout_title.xml文件我們完全可以使用merge標簽來減少RelativeLayout的層級。

<?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="wrap_content">

    <TextView
        android:id="@+id/text_title"
        android:layout_width="match_parent"
        android:layout_height="42dp"
        android:gravity="center"
        android:background="@color/colorPrimary"
        android:text="標題"
        android:textSize="18sp"
        android:textColor="@android:color/white"/>

</merge>

并不是說include標簽使用的時候都可以用merge標簽來優(yōu)化布局哦。也是要根據實際情況來確定的。

3.2、merge使用的時候有幾點是要特別注意的

  • merge必須放在布局文件的根節(jié)點上。
  • merge并不是一個ViewGroup,也不是一個View,它相當于聲明了一些視圖,等待被添加。
  • merge標簽被添加到A容器下,那么merge下的所有視圖將被添加到A容器下。
  • 因為merge不是View,所以對merge標簽設置的所有屬性都是無效的。

四、ViewStub

? ? ? ?ViewStub使用延遲加載的方式,避免資源的浪費,減少渲染時間,在需要的時候才加載View。即使將其放置于布局文件中,如果沒有進行加載那就為空,不像其它控件一樣只要布局文件中聲明就會存在。比如有這么個情況,網絡請求的界面網絡成功顯示內容信息,網絡請求失敗顯示失敗的界面。試想一下,如果網絡狀況良好,并不需要加載失敗頁面。這個時候使用ViewStub是一個非常好的選擇。

? ? ? ?ViewStub新增的屬性

  • android:layout:設置ViewStub被inflate的布局控件。ViewStub的內容布局。
  • android:inflateId:重寫ViewStub的父布局控件的Id。

? ? ? ?ViewStub使用的時候我們一般使用inflate()或者setVisibility(View.VISIBLE)使ViewStub可見。但是咱們還得注意,當setVisibility(int)或inflate()方法被調用之后,這個ViewStub在布局中將被使用指定的View(android:layout內容)替換。而且inflate過一遍的ViewStub,如果被隱藏之后再次想要顯示,將不能使用inflate()方法,但是可以再次使用setVisibility(int)方法設置為可見。

? ? ? ?用一個簡單的實例倆說明下ViewStub的使用。簡單的實現點擊"顯示空數據"按鈕,就會顯示ViewStub對應的布局。

activity_text.xml Activity布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="showEmpty"
        android:text="顯示空數據" />

    <ViewStub
        android:id="@+id/view_stub_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/view_stub" />

</LinearLayout>

view_stub_empty.xml ViewStub內容文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/hello_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="數據為空!" />
</FrameLayout>

Activity

public class TextActivity extends AppCompatActivity {

    private ViewStub mViewStubEmpty;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_text);
        mViewStubEmpty = findViewById(R.id.view_stub_empty);
    }

    public void showEmpty(View view) {
        mViewStubEmpty.inflate();
    }
}

ViewStub inflate()函數只能調用一次,重復調用會導致異常,這是因為ViewStub只要加載過一次,其自身就會被移除,把并自身所包含的內容全部傳給父布局。

ViewStub所替代的layout文件中不能使用merge標簽。


以上就是對布局優(yōu)化做了一個比較簡單的總結。

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容