android知識點——include、merge和ViewStub的佈局優化

先扯兩句

上次寫的部分主要還是一些封裝的抽象方法,這部分只是單純的爲我這種懶漢提供了便利罷了,而本次寫的內容呢,則是對Title的封裝,不過這篇是我自己寫的title封裝,並沒有使用Toolbar,也不是閒得沒事幹,之前使用Toolbar時UI要求title下邊加上一條1px的分割線,結果Toolbar的左側出現了16dp左右的空白無法處理,沒找到解決方案就放棄了Toolbar的使用。過些時間我會好好研究一下Toolbar,畢竟除去這點還是不錯的,畢竟自己封裝,對於我這種懶漢來說還是太麻煩了不是。而關於上述的情況,如果大家誰知道如何解決方法也歡迎分享,小老兒不勝感激。
好了,閒言少敘,老規矩還是先上我的Git。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)
並給大家展示個神器,叫Android知識點——目錄,好了,閒言少敘,下面進入正題。

正文

時間已經過去好久了,不知道大家還記得我之前的封裝嗎?好吧,反正我是忘得差不多了,只能重新查了查之前的博客。
首先是創建一個title_layout.xml存放title佈局:

<?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="@dimen/title_height">

    <RelativeLayout
        android:id="@+id/base_bg"
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_height"
        android:background="@color/blue">

        <ImageView
            android:id="@+id/base_back"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:padding="@dimen/size_13"
            android:src="@mipmap/back"
            android:tint="@android:color/white" />

        <TextView
            android:id="@+id/base_title"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="@string/title"
            android:textColor="@android:color/white"
            android:textSize="@dimen/size_20" />


        <ImageView
            android:id="@+id/base_right_icon2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_toLeftOf="@+id/base_right_icon1"
            android:contentDescription="@string/second_function_key"
            android:padding="@dimen/size_13"
            android:src="@mipmap/add"
            android:tint="@android:color/white"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/base_right_icon1"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:contentDescription="@string/first_function_key"
            android:padding="@dimen/size_13"
            android:src="@mipmap/more"
            android:tint="@android:color/white"
            android:visibility="gone" />

        <TextView
            android:id="@+id/base_right_text"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:gravity="center"
            android:text="@string/make_sure"
            android:textColor="@android:color/white"
            android:textSize="@dimen/size_17"
            android:visibility="gone" />
    </RelativeLayout>
</RelativeLayout>

隨後在BaseActivity的佈局文件activity_base.xml中添加如下佈局:

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

就完成title的添加,不過在項目中,有些時候並不需要title,或者是需要根據UI設計,去創建一些比較複雜的title,這個時候,就需要把這個刻板的title隱藏起來。因此這裏便給title添加了一個id

<include 
    android:id="@+id/base_title_layout"
    layout="@layout/title_layout"/>

隨後在BaseActivity中添加了如下方法:

/**
 * 隱藏頭佈局
 */
public void hideTitle() {
    if (baseTitle == null) {
        baseTitle = getView(R.id.base_title_layout);
    }
    baseTitle.setVisibility(View.GONE);
}

這樣就實現了上述的要求,可是雖然這裏加了隱藏,但是大家都知道,實際上這個title的佈局資源缺已經加載了,所以在這些頁面中,如果依舊這麼使用的話,就會造成資源的浪費。因此,在這次修改中,我將這個部分也做了修改,而使用到的控件就是ViewStub。
(其中getView方法看過我之前博客的應該知道,就是封裝的findViewById方法,大家可以直接使用findViewById替換,只是需要強轉一下類型即可)

佈局優化

說起ViewStub就不得不先說說重用佈局文件(也就是傳說中的佈局優化),其實網上可以查到大量的文章,而我卻是其中理解的相對淺顯的,所以如果大家想要具體瞭解的話,郭霖郭神的 Android最佳性能實踐(四)——佈局優化技巧
,而我這裏呢,而我呢,則根據自己的應用簡單說兩句就好了(無奈我這話癆的毛病,大家就忍耐一下這段“狗尾續貂”吧)

include

使用

include一看就知道是英語單詞,所以想知道他是做什麼的,最簡單的就是查查翻譯

image

除了那個包住感覺有點不靠譜以外,其他的意思還都差不多,那就簡單了,我就理解爲它的作用就是將關聯佈局內的所有控件都包括在當前include的位置。而既然可以把其他佈局引用過來,那樣自然也就複用了控件,從而優化了佈局代碼。
就比如上述我之前的封裝,就是單獨封裝了title佈局,然後在BaseActivity以及BaseFragment中進行了複用。而因爲這個佈局文件已經包含在了這個Iinclude標籤下,也就相當於其中的所有控件都在當前的這個頁面中。因此,在使用其中的控件時,就與當前佈局中的其他控件一樣,直接根據ID獲取即可。當然,在我封裝的這個框架裏,直接調用getView即可。

xml:

<include 
    android:id="@+id/base_title_layout"
    layout="@layout/title_layout"/>

java:

/**
 * 隱藏頭佈局
 */
public void hideTitle() {
    if (baseTitle == null) {
        baseTitle = getView(R.id.base_title_layout);
    }
    baseTitle.setVisibility(View.GONE);
}
注意事項

有些東西就是這樣,在提供方便的同時,自然就會出現一些隱患,而在使用include的時候,自然也有需要注意的地方。

  1. include在沒有設置約束參數的時候,會自動根據layout引入的去定義。而當我們需要更改的時候,則需要複寫這些約束參數即可,這也就是爲什麼我們常用的控件必須填寫“android:layout_width”與“android:layout_height”兩個參數,而當使用include的時候則不需要,因爲include複用佈局的最外層佈局已經夠約束好了其長寬,至於有什麼可以設置,郭神的博客中已經說明了:

非layout屬性則無法在標籤當中進行覆寫。另外需要注意的是,如果我們想要在標籤當中覆寫layout屬性,必須要將layout_width和layout_height這兩個屬性也進行覆寫,否則覆寫效果將不會生效。

  1. 在同一個佈局中,大家應該都會出現過id設置重複的時候吧,當然,不會再程序運行中出問題,那是因爲在程序運行之前,就AS已經告訴我們了,那樣做是不對滴。可是在使用include的時候,AS還無法那麼職能的判斷出究竟id是否設置重複,因此只有在運行過程中才會出現錯誤(會調用include中的控件,而忽略當前佈局中同id的控件),所以使用時一定要慎之又慎。

merge

同樣,看到這英文還是查一下什麼意思
這裏寫圖片描述

在使用git或者svn做版本控制的時候呢,我們稱其爲“和並”,這部分我在良秋 Android 佈局優化之include與merge中查到了如下介紹:

merge翻譯成中文是合併的意思,在Android中通過使用merge能夠減少視圖的節點數,從而減少視圖在繪製過程消耗的時間,達到提高UI性能的效果。

對於一個菜鳥來說,看到這麼玄乎其玄的解釋,第一反應就是蒙,如果是個勤懇好學的菜鳥,下一反應應該是去查一下什麼是“節點”。而懶點的估計就直接放棄了。實際上,對於我來說,就是所謂android開發中的常用佈局方式。
下面就開始研究這東西怎麼用,說實話,就我這種菜鳥,還真沒什麼機會用到merge,不是不知道怎麼用,其一真的是使用環境沒有include或者後面要說到的ViewStub那麼清晰,再者就是使用的條件比較苛刻,最後就是在該使用的時候,估計merge早不知道被我們忘哪去了。
下面先列舉一下良秋列舉的merge注意事項:

  1. merge必須放在佈局文件的根節點上;
  2. merge並不是一個ViewGroup,也不是一個View,它相當於聲明瞭一些視圖,等待被添加。
  3. merge標籤被添加到A容器下,那麼merge下的所有視圖將被添加到A容器下。
  4. 因爲merge標籤並不是View,所以在通過LayoutInflate.inflate方法渲染的時候, 第二個參數必須指定一個父容器,且第三個參數必須爲true,也就是必須爲merge下的視圖指定一個父親節點。
  5. 如果Activity的佈局文件根節點是FrameLayout,可以替換爲merge標籤,這樣,執行setContentView之後,會減少一層FrameLayout節點。
  6. 自定義View如果繼承LinearLayout,建議讓自定義View的佈局文件根節點設置成merge,這樣能少一層結點。
  7. 因爲merge不是View,所以對merge標籤設置的所有屬性都是無效的。

具體的內容參見良秋的博客說,有詳細的說明,這裏我就不加以贅述了,只是說兩點自己使用中走的彎路,算是對上述內容的一個佐證吧。

這裏寫圖片描述

上圖就是對圖1的佐證,也就是在佈局的時候Android Studio會給予的提示,但是對於一些開發是不那麼嚴謹的,可能注意不到merge的黃色提示,畢竟ImageView中沒有設置android:contentDescription(這個屬性是方便一些生理功能有缺陷的人使用應用程序的,比如一些視力有障礙的用戶,如果用戶安裝了輔助瀏覽工具比如TalkBack,TalkBack就會大聲朗讀出用戶目前正在瀏覽的內容。TextView控件TalkBack可以直接讀出裏面的內容(contentDescription的值),告訴用戶這個圖片到底是什麼)會出現這類提示,沒有使用到的一些佈局控件也會出現黃色提示(添加一個爲沒有子控件,且沒有id的RelativeLayout)等。畢竟大多數出現這個提示是因爲不建議,而不是不能用。
所以我也任性的在測試機上跑了一下,說不定能用呢,結果:
這裏寫圖片描述
於是我不得不死心了。
2.
作爲一個“生命不息,逗逼不止”的人,在使用中自然不可能只吃這一次虧,這次我創建了一個merge_test.xml佈局文件:

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

    <Button
        android:id="@+id/merge_btn"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"/>
</merge>

這次是在根節點上了吧,創建之後自然要使用了:

@SuppressLint("InflateParams")
    @Override
    protected void findViews() {
        view = (Button) LayoutInflater.from(context).inflate(R.layout.merge_test, null).findViewById(R.id.merge_btn);
        view.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.merge_btn:
                view.setText(count++ + "");
                break;
        }
    }

Shift + F10,merge,是時候展示你真正的技術了:

這裏寫圖片描述

mmp,跪求我心裏的陰影面積!難倒這就進入了一條死路嗎!!!好吧,作爲一個行業菜鳥,這個時候不得不去翻看大神們的博客以求幫助。

標籤是作爲標籤的一種輔助擴展來使用的,它的主要作用是爲了防止在引用佈局文件時產生多餘的佈局嵌套。

既然郭神已經在博客中這麼說了,作爲小菜鳥,暫時先這麼用着,至於有沒有其他的玩法,就看日後的使用與積累了。

ViewStub

看到這個詞呢,這裏就不翻譯了,實在是字面理解真不知道它是做什麼的。所以這次就直接說如何使用了。
其實在用法上,ViewStub與include還真有些類似,都是定義好的xml佈局,然後在使用的時候,通過控件直接引用佈局文件:

<ViewStub
    android:id="@+id/base_title_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout="@layout/title_layout" />

大家在看看include的佈局:

<include 
    android:id="@+id/base_title_layout"
    layout="@layout/title_layout"/>

是不是區別並不大,在這最基本的引用上:

  1. include引用佈局時使用的是layout="@layout/title_layout",而ViewStub使用的是android:layout="@layout/title_layout";
  2. ViewStub必須設置android:layout_width與android:layout_height,而從上面include中已經說明,include是不必須設置這兩個屬性的。
  3. 至於使用的環境,ViewStub是一些不常使用到的地方,這樣在不使用的時候,ViewStub可以節省資源,當需要使用的時候,通過代碼解析一下,便可以得到對應的佈局;而include則更偏向於同一個佈局的在多個頁面中的複用。

而關於ViewStub的是引用方法,在良秋 Android UI佈局優化之ViewStub中,關於有如下介紹:

  1. ViewStub是一個繼承了View類的視圖。
  2. ViewStub是不可見的,實際上是把寬高都設置爲0
  3. 可以通過佈局文件的android:inflatedId或者調用ViewStub的setInflatedId方法爲懶加載視圖的跟節點設置ID
  4. ViewStub視圖在首次調用setVisibility或者inflate方法之前,一直存在於視圖樹中
  5. 只需要調用ViewStub的setVisibility或者inflate方法即可顯示懶加載的視圖
  6. 調用setVisibility或者inflate方法之後,懶加載的視圖會把ViewStub從父節點中替換掉
  7. ViewStub的inflate只能被調用一次,第二次調用會拋出異常,setVisibility可以被調用多次,但不建議這麼做
  8. 爲ViewStub賦值的android:layout_屬性會替換待加載佈局文件的根節點對應的屬性
  9. inflate方法會返回待加載視圖的根節點

這已經基本上說明了ViewStub的用法以及相關的原理,反正至少作爲懶漢的我是沒辦法說的更明白了。當然,想了解更具體的原理的,可以去良秋的這篇博客中仔細學習,而單純的使用而言,則不需要那麼麻煩。
上面已經給出了ViewStub在xml文件中如何引用,不過往上翻多費勁啊,還是在下面再貼一下吧:

<ViewStub
    android:id="@+id/base_title_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout="@layout/title_layout" />

總共四行屬性(具體參數可以修改):

  1. android:layout="@layout/title_layout":所要引用佈局的layout xml文件;
  2. android:layout_width=“match_parent” android:layout_height=“wrap_content”:所要引用佈局的長寬設置;
  3. android:id="@+id/base_title_layout":需要解析時查找對應ViewStub的id。

誰都別問我爲什麼四行屬性,我這裏只寫了三點啊,至於強迫症患者的話,那我只能非常抱歉的說一句————你管我!!!
xml部分完成了,下面就該進行java部分了,也就是ViewStub的inflate,其實也很簡單:

/**
 * Title ViewStub
 */
private ViewStub titleStub;

/**
 * 控件初始化
 */
protected void initBaseView() {
    if (titleStub == null) {
        titleStub = getView(R.id.base_title_layout);
        titleStub.inflate();
    }
}

想要把ViewStub解析,總共分幾步?四步!

  1. 創建ViewStub對象;
  2. 判斷ViewStub是否已經解析過了,若沒有進行解析;
  3. 根據id找到需要解析的ViewStub;
  4. viewStub.inflate()執行解析操作;

如此,viewStub就展示到我們的界面上了,再對其中的各個控件進行操作,實際上就與include相同了,例如:

/**
 * 設置標題
 *
 * @param title    標題的文本
 * @param showBack 是否顯示返回鍵
 */
public void setTitle(String title, boolean showBack) {
    initBaseView();
    ((TextView) getView(R.id.base_title)).setText(title);
    if (showBack) {
        baseBack = getView(R.id.base_back);
        baseBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

最後的最後,思考了很久怎麼寫結尾的我,終於還是抵不過懶漢性格,還是直接摘一段郭神的內容放到這裏吧:

另外需要提醒大家一點,ViewStub所加載的佈局是不可以使用標籤的,因此這有可能導致加載出來的佈局存在着多餘的嵌套結構,具體如何去取捨就要根據各自的實際情況來決定了,對於那些隱藏的佈局文件結構相當複雜的情況,使用ViewStub還是一種相當不錯的選擇的,即使增加了一層無用的佈局結構,仍然還是利大於弊。

原本是打算將BaseActivity的封裝放到這裏的,不過寫得篇幅過長,而且佈局優化的部分總歸是要寫的,所以這裏就將佈局優化的部分單獨提出來寫了一篇,而BaseActivity封裝的內容只能放到後一篇再去寫了。

附錄

《一個Android工程的從零開始》- 目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章