詳細介紹安卓布局性能優化之(include 、merge、ViewStub)

我們在日常開發中,我們可能會遇到有很多相似的布局,如果每一個XML文件都寫一次,不說麻煩,代碼也顯得冗余,而且可讀性也很差.這時候就需要include 了,本編文章將會介紹include、merge和ViewStub標簽的用法供大家學習和參考。

include標簽

include標簽常用于將布局中的公共部分提取出來供其他layout共用,以實現布局模塊化,也是平常我們設計布局時用的最多的

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="wrap_content"
           android:gravity="center_horizontal"
           android:orientation="vertical"> 
<TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/textview"
        android:textSize="24sp"/> <EditText
        android:id="@+id/editText"
        android:hint="@string/divide"
        android:layout_width="300dp"
        android:layout_height="wrap_content"/> </LinearLayout> 

<?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"
                android:gravity="center_horizontal"
    > <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="TextView_Relative"
        android:textSize="24sp"/> <EditText
        android:id="@+id/editText"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"
        android:hint="@string/divide"/> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/tb_toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="#00f"
    app:theme="@style/AppTheme"
    app:title="這是一個ToolBar"
    app:titleTextColor="@android:color/white"/>
    

1.2、Activity的XML布局文件調用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"
    android:orientation="vertical">
 <!--測試layout和<include>都設置ID的情況-->
<include
        android:id="@+id/tb_toolbar"
        layout="@layout/include_toolbar"/> 
<!--如果只有單個include 這樣寫就可以,加載的布局的子View,直接findViewByID就能找到--> 
   <include layout="@layout/include_text"/> 
   <!--如果有多個include,需要添加ID屬性--> <include
        android:id="@+id/include_text1"
        layout="@layout/include_text"/> 
<!--這個layout用RelativeLayout 實現-->
 <!--如果要使用layout_margin這樣的屬性,要同時加上layout_w/h屬性,不然沒反應--> <include
        android:id="@+id/include_text2"
        layout="@layout/include_text_relative"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="50dp"/>
         </LinearLayout>

1.3、Activity中調用include標簽layout中的子View:

 private void initView() { 
 //如果include布局根容器和include標簽中的id設置的是不同的值,這里獲取的mToolbar值將為null 
 Toolbar mToolbar = (Toolbar) findViewById(R.id.tb_toolbar); setSupportActionBar(mToolbar); 
 //普通include標簽用法,直接拿子View屬性實現
  TextView textView = (TextView) findViewById(R.id.textView); textView.setText("不加ID實現的include標簽"); 
  //多個include標簽用法,添加ID,findViewByID找到layout,再找子控件 
  View view_include = findViewById(R.id.include_text1); TextView view_include_textView = (TextView) view_include.findViewById(R.id.textView); view_include_textView.setText("加了ID實現的include標簽"); 
  //多個include標簽用法,添加ID,findViewByID找到layout,再找子控件
   View view_include_Relative = findViewById(R.id.include_text2); TextView view_textView_relative = (TextView) view_include_Relative.findViewById(R.id.textView); view_textView_relative.setText("加了ID實現的include標簽(RelaviteLayout)"); }

==include使用注意==

  • 一個xml布局文件有多個include標簽需要設置ID,才能找到相應子View的控件,否則只能找到第一個include的layout布局,以及該布局的控件
  • include標簽如果使用layout_xx屬性,會覆蓋被include的xml文件根節點對應的layout_xx屬性,建議在include標簽調用的布局設置好寬高位置,防止不必要的bug
  • include 添加id,會覆蓋被include的xml文件根節點ID,這里建議include和被include覆蓋的xml文件根節點設置同名的ID,不然有可能會報空指針異常
  • 如果要在include標簽下使用RelativeLayout,如layout_margin等其他屬性,記得要同時設置layout_width和layout_height,不然其它屬性會沒反應

merge 標簽

merge標簽主要用于輔助include標簽,在使用include后可能導致布局嵌套過多,多余的layout節點或導致解析變慢(可通過hierarchy viewer工具查看布局的嵌套情況)

官方文檔說明:merge用于消除視圖層次結構中的冗余視圖,例如根布局是Linearlayout,那么我們又include一個LinerLayout布局就沒意義了,反而會減慢UI加載速度

merge 官方文檔

  • merge標簽常用場景:

    1.根布局是FrameLayout且不需要設置background或padding等屬性,可以用merge代替,因為Activity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一個。
    2.某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合并到主布局中。
    3.自定義View如果繼承LinearLayout(ViewGroup),建議讓自定義View的布局文件根布局設置成merge,這樣能少一層結點。

merge標簽使用:

在XML布局文件的根布局如RelativeLayout直接改成merge即可

merge使用注意

1.因為merge標簽并不是View,所以在通過LayoutInflate.inflate()方法渲染的時候,第二個參數必須指定一個父容器,且第三個參數必須為true,也就是必須為merge下的視圖指定一個父親節點.
2.因為merge不是View,所以對merge標簽設置的所有屬性都是無效的.
3.注意如果include的layout用了merge,調用include的根布局也使用了merge標簽,那么就失去布局的屬性了
4.merge標簽必須使用在根布局
5.ViewStub標簽中的layout布局不能使用merge標簽

ViewStub 標簽

我們在做安卓項目的時候,經常會有一個使用場景:需要在運行時根據數據動態決定顯示或隱藏某個View和布局。
上述場景,我們通常的解決方案就是:就是把可能用到的View先寫在布局里,再初始化其可見性都設為View.GONE,然后在代碼中根據數據動態的更改它的可見性。
雖然這樣的實現,邏輯簡單而且控制起來比較靈活;但是也存在一定的缺點耗費資源。
ViewStub 標簽最大的優點是當你需要時才會加載,使用它并不會影響UI初始化時的性能.各種不常用的布局像進度條、顯示錯誤消息等可以使用ViewStub標簽,以減少內存使用量,加快渲染速度.ViewStub是一個不可見的,實際上是把寬高設置為0的View.效果有點類似普通的view.setVisible(),但性能體驗提高不少
第一次初始化時,初始化的是ViewStub View,當我們調用inflate()或setVisibility()后會被remove掉,然后在將其中的layout加到當前view hierarchy中。

先來看看布局,一個是主布局,里面只定義二個ViewStub,一個用來控制TextView一個用來控制ImageView,另外就是一個是為顯示文字的做的TextView布局,一個是為ImageView而做的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:gravity="center_horizontal">
  <ViewStub 
    android:id="@+id/viewstub_demo_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dip"
    android:layout_marginRight="5dip"
    android:layout_marginTop="10dip"
    android:layout="@layout/viewstub_demo_text_layout"/>
  <ViewStub 
    android:id="@+id/viewstub_demo_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dip"
    android:layout_marginRight="5dip"
    android:layout="@layout/viewstub_demo_image_layout"/>
</LinearLayout>

為TextView的布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">
        <TextView
            android:id="@+id/viewstub_demo_textview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#aa664411"
            android:textSize="16sp"/>
    </LinearLayout>

為ImageView的布局:

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

下面來看代碼,決定來顯示哪一個,只需要找到相應的ViewStub然后調用其infalte()就可以獲得相應想要的布局:

    public class ViewStubDemoActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.viewstub_demo_activity);
            if ((((int) (Math.random() * 100)) & 0x01) == 0) {
                // to show text
                // all you have to do is inflate the ViewStub for textview
                ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);
                stub.inflate();
                TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);
                text.setText("The tree of liberty must be refreshed from time to time" +
                        " with the blood of patroits and tyrants! Freedom is nothing but " +
                        "a chance to be better!");
            } else {
                // to show image
                // all you have to do is inflate the ViewStub for imageview
                ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);
                stub.inflate();
                ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);
                image.setImageResource(R.drawable.happy_running_dog);
            }
        }
    }

ViewStub標簽使用注意

  1. ViewStub標簽不支持merge標簽
  2. ViewStub的inflate只能被調用一次,第二次調用會拋出異常,setVisibility可以被調用多次,但不建議這么做(ViewStub 調用過后,可能被GC掉,再調用setVisibility()會報異常)
  3. 為ViewStub賦值的android:layout_XX屬性會替換待加載布局文件的根節點對應的屬性

擴展:

Space組件

在ConstraintLayout出來前,我們寫布局都會使用到大量的margin或padding,但是這種方式可讀性會很差,加一個布局嵌套又會損耗性能

鑒于這種情況,我們可以使用space,使用方式和View一樣,不過主要用來占位置,不會有任何顯示效果

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容