Android進階——布局優(yōu)化之靈活借助ViewStub實現(xiàn)懶加載

引言

相信在開發(fā)Android App的過程中秘豹,我們會常常遇到這樣的業(yè)務需求携御,需要在運行時根據(jù)數(shù)據(jù)動態(tài)決定顯示或隱藏某個View和布局。通常就是把可能用到的View先寫在布局里既绕,再初始化其可見性都設為View.GONE啄刹,然后在代碼中根據(jù)數(shù)據(jù)動態(tài)的更改它的可見性。雖然這樣的實現(xiàn)凄贩,邏輯簡單而且控制起來比較靈活誓军。但是也存在一定的缺點耗費資源,即使把View的初始可見View.GONE但是在Inflate布局的時候View仍然會被Inflate疲扎,即說仍然會創(chuàng)建對象昵时,會被實例化,會被設置屬性從而導致耗費內(nèi)存等資源椒丧。今天推薦一種新的機制——ViewStub壹甥,但要根據(jù)自己的業(yè)務需求來靈活使用。

一壶熏、ViewStub概述

ViewStub 直接繼承自View句柠,是一種不可見,0大小的可以在運行的時候再加載的View(A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime)棒假。僅只有在調(diào)用inflate()進行映射內(nèi)容布局之后(值得注意的是ViewStub只能inflate一次溯职,再次進行inflate的時候會報異常)或者設置為Visibility時才可見,功能上可以看成是高級的< include >標簽(一個把其它布局資源包含進某個特定的布局中帽哑,有點類似其他開發(fā)語言中的模板概念)缸榄。

二、ViewStub的特點祝拯、適用場景及注意事項

1甚带、ViewStub的一些特點

  • ViewStub只能被Inflate一次,inflate之后ViewStub對象就會被置為空佳头。即某個被ViewStub指定的布局被Inflate后鹰贵,就不能夠再通過ViewStub來控制它了。

  • ViewStub只能用來Inflate一個布局文件康嘉,而不是某個具體的View碉输,當然也可以把View寫在某個布局文件中。

2亭珍、ViewStub的適用場景及注意事項

  • 在程序的運行期間敷钾,某個布局在被Inflate后枝哄,就不會有變化,除非重新啟動阻荒。因為ViewStub只能Inflate一次挠锥,inflate之后就不能直接使用ViewStub來控制布局。

  • 想要控制顯示與隱藏的是一個布局文件侨赡,而非某個View蓖租。因為設置給ViewStub的只能是某個布局文件的Id,所以無法讓它來直接控制某個View羊壹。所以蓖宦,如果想要控制某個View的顯示與隱藏,抑或想要在運行時不斷的顯示與隱藏某個布局或View油猫,只能使用View的可見性來控制稠茂。

二、ViewStub的使用步驟

ViewStub支持在程序運行的過程中通過懶加載的模式inflate布局資源中情妖。只有當一個ViewStub的inflate()方法被調(diào)用或者被設為View.VISIBILITY時睬关,此時ViewStub會把設定的布局才會被創(chuàng)建對應的對象和實例化,并替換當前ViewStub的位置鲫售,顯示相應的效果共螺。雖然一開始ViewStub就存在于視圖樹中该肴,但是直到setVisibility(int)或inflate()方法被調(diào)用時才消耗資源情竹,否則是不加載控件的,因此消耗的資源小匀哄。這就是所謂的"懶加載”秦效。和< include >標簽一樣可以看成是一個“占位符“,可以看成自身是不呈現(xiàn)任何UI效果的視圖容器涎嚼,主要就是用于存放真實的布局和視圖阱州,所以除了設置必要的尺寸屬性和位置之外,通常必須設置三個重要屬性和一個回調(diào)監(jiān)聽接口:

  • android:id——ViewStub 自身的Id法梯,無論是否被inflate苔货,都可以通過findViewById拿到對應的ViewStub控件本身。

  • android:inflatedId——ViewStub設置的被映射的布局文件中的跟節(jié)點的Id立哑,inflate之后可以通過findViewById獲取到對應的被映射的布局對象夜惭。

  • android:layout——將要映射的布局文件名,注意和include 標簽里的區(qū)分(include標簽是layout:)

  • ViewStub.OnInflateListener——當ViewStub成功映射預先設置的布局會觸發(fā)回調(diào)(Listener used to receive a notification after a ViewStub has successfully inflated its layout resource)

ViewStub的優(yōu)勢在于在某些場景中铛绰,并不一定需要把所有的內(nèi)容都展示出來诈茧,可以隱藏一些View視圖,待用戶需要展示的時候再加載到當前的Layout中捂掰,這個時候就可以用到ViewStub這個控件了敢会,這樣可以減少資源的消耗曾沈,使最初的加載速度變快。

三鸥昏、簡單使用ViewStub

1塞俱、首先建立將要被映射的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container_erro_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@mipmap/bg_exit_dialog"
    android:layout_gravity="center"
    android:orientation="vertical">
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@mipmap/ic_erro_tips"
        android:layout_marginBottom="16dp"/>
    <TextView
        android:id="@+id/tv_erro_tips"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#ffffff"
        />

</LinearLayout>

2、建立主布局引入ViewStub

<?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="match_parent"
    android:background="@mipmap/bg_activity">
    <ViewStub
        android:id="@+id/contentPanel"
        android:inflatedId="@+id/inflatedStart"
        android:layout="@layout/layout_no_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="showViewStub"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="48dp"
        android:text="showViewStub"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="hideViewStub"
        android:layout_alignParentBottom="true"
        android:text="hideViewStub"/>
</RelativeLayout>

3互广、實現(xiàn)MainActivity

package com.crazymo.swiperefreshlayout;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewStub;
import android.widget.LinearLayout;
import android.widget.TextView;

public class ViewStubActivity extends AppCompatActivity implements ViewStub.OnInflateListener {
    private ViewStub viewStub;
    private LinearLayout parentContainer;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_stub);
        init();
    }

    private void init() {
        viewStub= (ViewStub) findViewById(R.id.contentPanel);
        viewStub.setOnInflateListener(this);
    }

    @Override
    public void onInflate(ViewStub stub, View inflated) {
        //inflate ViewStub的時候顯示
        Log.e("ViewStub","ViewStub is loaded! viewStub==null"+(viewStub==null));
    }

    public void showViewStub(View view){
        showViewStub();
    }

    private void showViewStub() {
        try {
            Log.e("ViewStub","ViewStub load before! viewStub==null"+(viewStub==null));
            parentContainer = (LinearLayout) viewStub.inflate();
            textView = (TextView) parentContainer.findViewById(R.id.tv_erro_tips);
            textView.setText("不好意思敛腌,還未錄入任何數(shù)據(jù)");
        }catch (Exception e){
            if(parentContainer==null) {
                parentContainer = (LinearLayout) findViewById(R.id.inflatedStart);
            }
            if(textView==null) {
                textView = (TextView) parentContainer.findViewById(R.id.tv_erro_tips);
            }
            textView.setText("不好意思,還未錄入任何數(shù)據(jù)");
            viewStub.setVisibility(View.VISIBLE);
        }
    }

    public void hideViewStub(View view){
        viewStub.setVisibility(View.GONE);
    }
}

初始化和隱藏ViewStub之后MainActivity的布局層次結構
[圖片上傳失敗...(image-560006-1512722888068)]
顯示ViewStub之后


這里寫圖片描述

#引言
相信在開發(fā)Android App的過程中惫皱,我們會常常遇到這樣的業(yè)務需求像樊,需要在運行時根據(jù)數(shù)據(jù)動態(tài)決定顯示或隱藏某個View和布局。通常就是把可能用到的View先寫在布局里旅敷,再初始化其可見性都設為View.GONE生棍,然后在代碼中根據(jù)數(shù)據(jù)動態(tài)的更改它的可見性。雖然這樣的實現(xiàn)媳谁,邏輯簡單而且控制起來比較靈活涂滴。但是也存在一定的缺點耗費資源,即使把View的初始可見View.GONE但是在Inflate布局的時候View仍然會被Inflate晴音,即說仍然會創(chuàng)建對象柔纵,會被實例化,會被設置屬性從而導致耗費內(nèi)存等資源锤躁。今天推薦一種新的機制——ViewStub搁料,但要根據(jù)自己的業(yè)務需求來靈活使用。

一系羞、ViewStub概述

ViewStub 直接繼承自View郭计,是一種不可見,0大小的可以在運行的時候再加載的View(A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime)椒振。僅只有在調(diào)用inflate()進行映射內(nèi)容布局之后(值得注意的是ViewStub只能inflate一次昭伸,再次進行inflate的時候會報異常)或者設置為Visibility時才可見,功能上可以看成是高級的< include >標簽(一個把其它布局資源包含進某個特定的布局中澎迎,有點類似其他開發(fā)語言中的模板概念)庐杨。

二、ViewStub的特點夹供、適用場景及注意事項

1灵份、ViewStub的一些特點

  • ViewStub只能被Inflate一次,inflate之后ViewStub對象就會被置為空罩引。即某個被ViewStub指定的布局被Inflate后各吨,就不能夠再通過ViewStub來控制它了。

  • ViewStub只能用來Inflate一個布局文件,而不是某個具體的View揭蜒,當然也可以把View寫在某個布局文件中横浑。

2、ViewStub的適用場景及注意事項

  • 在程序的運行期間屉更,某個布局在被Inflate后徙融,就不會有變化,除非重新啟動瑰谜。因為ViewStub只能Inflate一次欺冀,inflate之后就不能直接使用ViewStub來控制布局。

  • 想要控制顯示與隱藏的是一個布局文件萨脑,而非某個View隐轩。因為設置給ViewStub的只能是某個布局文件的Id,所以無法讓它來直接控制某個View渤早。所以职车,如果想要控制某個View的顯示與隱藏,抑或想要在運行時不斷的顯示與隱藏某個布局或View鹊杖,只能使用View的可見性來控制悴灵。

二、ViewStub的使用步驟

ViewStub支持在程序運行的過程中通過懶加載的模式inflate布局資源中骂蓖。只有當一個ViewStub的inflate()方法被調(diào)用或者被設為View.VISIBILITY時积瞒,此時ViewStub會把設定的布局才會被創(chuàng)建對應的對象和實例化,并替換當前ViewStub的位置登下,顯示相應的效果茫孔。雖然一開始ViewStub就存在于視圖樹中,但是直到setVisibility(int)或inflate()方法被調(diào)用時才消耗資源庐船,否則是不加載控件的银酬,因此消耗的資源小嘲更。這就是所謂的"懶加載”筐钟。和< include >標簽一樣可以看成是一個“占位符“,可以看成自身是不呈現(xiàn)任何UI效果的視圖容器赋朦,主要就是用于存放真實的布局和視圖篓冲,所以除了設置必要的尺寸屬性和位置之外,通常必須設置三個重要屬性和一個回調(diào)監(jiān)聽接口:

  • android:id——ViewStub 自身的Id宠哄,無論是否被inflate壹将,都可以通過findViewById拿到對應的ViewStub控件本身。

  • android:inflatedId——ViewStub設置的被映射的布局文件中的跟節(jié)點的Id毛嫉,inflate之后可以通過findViewById獲取到對應的被映射的布局對象诽俯。

  • android:layout——將要映射的布局文件名,注意和include 標簽里的區(qū)分(include標簽是layout:)

  • ViewStub.OnInflateListener——當ViewStub成功映射預先設置的布局會觸發(fā)回調(diào)(Listener used to receive a notification after a ViewStub has successfully inflated its layout resource)

ViewStub的優(yōu)勢在于在某些場景中承粤,并不一定需要把所有的內(nèi)容都展示出來暴区,可以隱藏一些View視圖闯团,待用戶需要展示的時候再加載到當前的Layout中,這個時候就可以用到ViewStub這個控件了仙粱,這樣可以減少資源的消耗房交,使最初的加載速度變快。

三伐割、簡單使用ViewStub

1候味、首先建立將要被映射的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container_erro_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@mipmap/bg_exit_dialog"
    android:layout_gravity="center"
    android:orientation="vertical">
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@mipmap/ic_erro_tips"
        android:layout_marginBottom="16dp"/>
    <TextView
        android:id="@+id/tv_erro_tips"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#ffffff"
        />

</LinearLayout>

2、建立主布局引入ViewStub

<?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="match_parent"
    android:background="@mipmap/bg_activity">
    <ViewStub
        android:id="@+id/contentPanel"
        android:inflatedId="@+id/inflatedStart"
        android:layout="@layout/layout_no_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="showViewStub"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="48dp"
        android:text="showViewStub"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="hideViewStub"
        android:layout_alignParentBottom="true"
        android:text="hideViewStub"/>
</RelativeLayout>

3隔心、實現(xiàn)MainActivity

package com.crazymo.swiperefreshlayout;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewStub;
import android.widget.LinearLayout;
import android.widget.TextView;

public class ViewStubActivity extends AppCompatActivity implements ViewStub.OnInflateListener {
    private ViewStub viewStub;
    private LinearLayout parentContainer;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_stub);
        init();
    }

    private void init() {
        viewStub= (ViewStub) findViewById(R.id.contentPanel);
        viewStub.setOnInflateListener(this);
    }

    @Override
    public void onInflate(ViewStub stub, View inflated) {
        //inflate ViewStub的時候顯示
        Log.e("ViewStub","ViewStub is loaded! viewStub==null"+(viewStub==null));
    }

    public void showViewStub(View view){
        showViewStub();
    }

    private void showViewStub() {
        try {
            Log.e("ViewStub","ViewStub load before! viewStub==null"+(viewStub==null));
            parentContainer = (LinearLayout) viewStub.inflate();
            textView = (TextView) parentContainer.findViewById(R.id.tv_erro_tips);
            textView.setText("不好意思白群,還未錄入任何數(shù)據(jù)");
        }catch (Exception e){
            if(parentContainer==null) {
                parentContainer = (LinearLayout) findViewById(R.id.inflatedStart);
            }
            if(textView==null) {
                textView = (TextView) parentContainer.findViewById(R.id.tv_erro_tips);
            }
            textView.setText("不好意思,還未錄入任何數(shù)據(jù)");
            viewStub.setVisibility(View.VISIBLE);
        }
    }

    public void hideViewStub(View view){
        viewStub.setVisibility(View.GONE);
    }
}

初始化和隱藏ViewStub之后MainActivity的布局層次結構
[圖片上傳失敗...(image-af0d98-1512723146954)]
顯示ViewStub之后


這里寫圖片描述
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硬霍,一起剝皮案震驚了整個濱河市川抡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌须尚,老刑警劉巖崖堤,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耐床,居然都是意外死亡密幔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門撩轰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胯甩,“玉大人,你說我怎么就攤上這事堪嫂≠梭铮” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵皆串,是天一觀的道長淹办。 經(jīng)常有香客問我,道長恶复,這世上最難降的妖魔是什么怜森? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘棠隐。我一直安慰自己幽邓,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钢拧,像睡著了一般拯爽。 火紅的嫁衣襯著肌膚如雪培己。 梳的紋絲不亂的頭發(fā)上糜烹,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音漱凝,去河邊找鬼疮蹦。 笑死,一個胖子當著我的面吹牛茸炒,可吹牛的內(nèi)容都是我干的愕乎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壁公,長吁一口氣:“原來是場噩夢啊……” “哼感论!你這毒婦竟也來了?” 一聲冷哼從身側響起紊册,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤比肄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后囊陡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芳绩,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年撞反,在試婚紗的時候發(fā)現(xiàn)自己被綠了妥色。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡遏片,死狀恐怖嘹害,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吮便,我是刑警寧澤笔呀,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站髓需,受9級特大地震影響许师,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜授账,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一枯跑、第九天 我趴在偏房一處隱蔽的房頂上張望惨驶。 院中可真熱鬧白热,春花似錦、人聲如沸粗卜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至攻臀,卻和暖如春焕数,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刨啸。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工堡赔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人设联。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓善已,卻偏偏與公主長得像,于是被迫代替她去往敵國和親离例。 傳聞我的和親對象是個殘疾皇子换团,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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