布局優(yōu)化include, viewstub, merge

1 include

視圖引入躺孝,可以配合merge使用
重用布局

2.merge

<merge/>標簽在UI的結構優(yōu)化中起著非常重要的作用享扔,它可以刪減多余的層級,優(yōu)化UI植袍。<merge/>多用于替換FrameLayout或者當一個布局包含另一個時伪很,<merge/>標簽消除視圖層次結構中多余的視圖組。例如你的主布局文件是垂直布局奋单,引入了一個垂直布局的include,這是如果include布局使用的LinearLayout就沒意義了猫十,使用的話反而減慢你的UI表現(xiàn)览濒。這時可以使用<merge/>標簽優(yōu)化呆盖。
減少視圖層級
步驟如下:
創(chuàng)建merge視圖

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="123" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="123" />
</merge>

視圖引入

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context="com.example.ticker.myapplication.MainActivity">
    <include layout="@layout/merge_layout"></include>
</LinearLayout>
3.ViewStub

當我們需要根據(jù)某個條件控制某個View的顯示或者隱藏的時候,通常是把可能用到的View都寫在布局上贷笛,然后設置可見性為View.GONE或View.InVisible 应又,之后在代碼中根據(jù)條件動態(tài)控制可見性。雖然操作簡單乏苦,但是耗費資源株扛,因為即便該view不可見,仍會被父窗體繪制汇荐,仍會創(chuàng)建對象洞就,仍會被實例化,仍會被設置屬性掀淘。
引入一個外部布局旬蟋,與上面不同的是,viewstub引入的布局默認不會擴張革娄,即既不會占用顯示也不會占用位置倾贰,從而在解析layout時節(jié)省cpu和內(nèi)存。ViewStub是一個不可見的拦惋,大小為0的視圖匆浙,只有給他設置成了View.Visible或調用了它的inflate()之后才會填充布局資源〔扪可以在運行過程中延時加載布局資源首尼,inflate 方法只能被調用一次,因為調用后viewStub對象就被移除了視圖樹叹放;

注:ViewStub目前有個缺陷就是還不支持 <merge /> 標簽饰恕。

使用步驟如下:
1、創(chuàng)建需要加載的布局

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

在需要引入的xml中引入

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.ticker.myapplication.MainActivity">

<Button
            android:id="@+id/btn_vs_showView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="顯示ViewStub"/>
    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="match_parent"
        android:layout="@layout/viewstub_layout"
        android:layout_height="match_parent" />
</RelativeLayout>

3井仰、使用

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewStub viewStub = (ViewStub) findViewById(R.id.viewstub);
        View view = viewStub.inflate();
        Button btn_show = (Button) findViewById(R.id.btn_vs_showView);
    }


      @Override
      public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_vs_showView:

                //inflate 方法只能被調用一次埋嵌,因為調用后viewStub對象就被移除了視圖樹;
                // 所以俱恶,如果此時再次點擊顯示按鈕雹嗦,就會崩潰,錯誤信息:ViewStub must have a non-null ViewGroup viewParent合是;
                // 所以使用try catch ,當此處發(fā)現(xiàn)exception 的時候了罪,在catch中使用setVisibility()重新顯示
                try {
                    View iv_vsContent = viewStub.inflate();     //inflate 方法只能被調用一次,
                    hintText = (TextView) iv_vsContent.findViewById(R.id.tv_vsContent);
                    //                    hintText.setText("沒有相關數(shù)據(jù)聪全,請刷新");
                } catch (Exception e) {
                    viewStub.setVisibility(View.VISIBLE);
                } finally {
                    hintText.setText("沒有相關數(shù)據(jù)泊藕,請刷新");
                }
                break;
           
        }
    }
}
4、使用總結

1)ViewStub 引用布局時使用layout 屬性难礼,取值@layout/xxxxx

2)InflateId 表示給被引用的布局的id娃圆。也就是被控制顯示或隱藏的布局的id.

3)如果不寫InflateId ,如果需要的話也可以直接在被引用的布局中給出id屬性

4)inflate() 方法只能被調用一次玫锋,如果再次調用會報異常信息 ViewStub must have a non-null ViewGroup viewParent。

這是因為讼呢,當ViewStub 調用inflate() 將其引用的 布局/view 展示出來之后撩鹿,ViewStub本身就會從視圖樹中被移除,此時viewStub 就獲取不到他的 父布局悦屏, 而 inflate() 方法中节沦,上來就需要獲取它的父布局,然后根據(jù)父布局是否為空再去執(zhí)行具體的填充邏輯础爬,如果為空就報上面的錯甫贯,所以,inflate() 之后如果還想再次顯示ViewStub 引用的布局/view 就需要 在調用inflate() 的時候try catch幕帆,當 catch 到異常的時候获搏,調用setVisibility()設置viewStub 的View.Visible即可。ViewStub類中Inflate() 的具體邏輯如下:

public View inflate() {
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent;
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater;
            } else {
                factory = LayoutInflater.from(mContext);
            }
            final View view = factory.inflate(mLayoutResource, parent,
                    false);

            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }

            final int index = parent.indexOfChild(this);
            parent.removeViewInLayout(this);

            final ViewGroup.LayoutParams layoutParams = getLayoutParams();
            if (layoutParams != null) {
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            mInflatedViewRef = new WeakReference<View>(view);

            if (mInflateListener != null) {
                mInflateListener.onInflate(this, view);
            }

            return view;
        } else {
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}
  1. ViewStub的setVisibility()中也調用了inflate(),但是為什么多次調用setVisibility()不會導致崩潰呢失乾?

ViewStub 的setVisibility() 方法中常熙,會先判斷 WeakReference 類型的成員變量 mInflatedViewRef 是否為空。第一次調用setVisibility()的時候碱茁,mInflatedViewRef并沒有初始化裸卫,也就是說是null,那么這時候就會走inflate(),在inflate() 方法中給被填充起來的布局/view創(chuàng)建一個WeakReference弱引用纽竣,并賦值給mInflatedViewRef墓贿,從而完成mInflatedViewRef的初始化。當?shù)诙巫遱etVisibility() 的時候蜓氨,mInflatedViewRef已經(jīng)不再是null,就會調用 WeakReference 的父類Reference 中的get() 方法獲取該引用指向的實體對象聋袋,也就是說通過get() 拿到 被填充的view對象,然后再走View類的setVisibility()穴吹。ViewStub類中的setVisibility()具體實現(xiàn)如下:

@Override
@android.view.RemotableViewMethod
public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {
        View view = mInflatedViewRef.get();
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}

7)ViewStub 的inflate() 只能被調用一次幽勒, 如果想控制/修改 被填充布局中的內(nèi)容并重復顯示被填充的view,就用try 將viewStub.inflate() 以及修改內(nèi)容的代碼包裹起來港令,并在catch 中setVisibility.

  1. 在xml 中定義ViewStub 節(jié)點時啥容,內(nèi)部不能包含其他節(jié)點,也就是說顷霹,ViewStub 是一個自閉合節(jié)點咪惠,如果一個布局/view如果想通過ViewStub顯示,只能定義在單獨的xml 文件中淋淀。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遥昧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渠鸽,老刑警劉巖叫乌,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異徽缚,居然都是意外死亡,警方通過查閱死者的電腦和手機革屠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門凿试,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人似芝,你說我怎么就攤上這事那婉。” “怎么了党瓮?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵详炬,是天一觀的道長。 經(jīng)常有香客問我寞奸,道長呛谜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任枪萄,我火速辦了婚禮隐岛,結果婚禮上,老公的妹妹穿的比我還像新娘瓷翻。我一直安慰自己聚凹,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布齐帚。 她就那樣靜靜地躺著妒牙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪对妄。 梳的紋絲不亂的頭發(fā)上湘今,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音饥伊,去河邊找鬼象浑。 笑死,一個胖子當著我的面吹牛琅豆,可吹牛的內(nèi)容都是我干的愉豺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼茫因,長吁一口氣:“原來是場噩夢啊……” “哼蚪拦!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驰贷,失蹤者是張志新(化名)和其女友劉穎盛嘿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體括袒,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡次兆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锹锰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芥炭。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恃慧,靈堂內(nèi)的尸體忽然破棺而出园蝠,到底是詐尸還是另有隱情,我是刑警寧澤痢士,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布彪薛,位于F島的核電站,受9級特大地震影響怠蹂,放射性物質發(fā)生泄漏善延。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一褥蚯、第九天 我趴在偏房一處隱蔽的房頂上張望挚冤。 院中可真熱鬧,春花似錦赞庶、人聲如沸训挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澜薄。三九已至,卻和暖如春摊册,著一層夾襖步出監(jiān)牢的瞬間肤京,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工茅特, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忘分,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓白修,卻偏偏與公主長得像妒峦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兵睛,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容