Android 控件ViewStub

Android ViewStub

引言:一個(gè)可用于性能優(yōu)化的控件垛膝。

時(shí)間:2017年09月21日

作者:JustDo23

Github:https://github.com/JustDo23

官網(wǎng):https://developer.android.com/reference/android/view/ViewStub.html

01. 簡介

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.

控件 ViewStub 是一個(gè)不可見的,零尺寸的 View 它可以在運(yùn)行時(shí)進(jìn)行延遲加載。

When a ViewStub is made visible , or when inflate() is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.

當(dāng) ViewStubsetVisibility(int) 方法或者 inflate() 方法被調(diào)用,它會(huì)加載被指定的布局在父布局中將自己替換為加載的布局。

Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked.

替換后,控件 ViewStub 會(huì)從布局樹移除胳赌。

The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.

控件 ViewStub布局屬性會(huì)傳遞給被加載的布局。

  • 因此匙隔,不是必須顯示的布局使用 ViewStub 代替后減少界面首次加載時(shí)資源消耗疑苫,提升最初加載速度。

02. 用法

  1. 布局

    <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:inflatedId="@+id/titleBar"
        android:layout="@layout/just_title" />
    
    • android:layout 指定被加載替換的布局
    • android:inflatedId 指定被加載替換的布局的 id
  2. 加載

    viewStub = (ViewStub) findViewById(R.id.viewStub);
    View inflated = stub.inflate();
    
    • 官方推薦加載首選方法
    • 調(diào)用 inflate() 方法后布局被加載替換同時(shí)返回布局對(duì)象纷责。避免了使用 findViewById() 方法捍掺。
    • inflate() 方法只能調(diào)用一次,調(diào)用被移除而沒有了父布局再膳。第二次調(diào)用會(huì)拋出異常 ViewStub must have a non-null ViewGroup viewParent 挺勿。

03. 示例

  1. 主界面布局

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
    
      <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:inflatedId="@+id/titleBar"
        android:layout="@layout/just_title" />
    
      <Button
        android:id="@+id/bt_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:text="Show"
        android:textAllCaps="false" />
    
      <Button
        android:id="@+id/bt_hide"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/bt_show"
        android:layout_marginTop="10dp"
        android:text="Hide"
        android:textAllCaps="false" />
    
    </RelativeLayout>
    
  2. 被替換布局 just_title.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="50dp"
      android:background="@color/colorPrimary">
    
      <TextView
        android:id="@+id/tv_show_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        tools:text="Title" />
    
    </RelativeLayout>
    
  3. 界面加載

    public class ViewStubActivity extends AppCompatActivity implements View.OnClickListener {
    
      private ViewStub viewStub;// 占位控件
      private Button bt_show;// 顯示按鈕
      private Button bt_hide;// 隱藏按鈕
      private TextView tv_show_title;// 標(biāo)題
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_stub);
        viewStub = (ViewStub) findViewById(R.id.viewStub);// 尋找控件
        bt_show = (Button) findViewById(R.id.bt_show);
        bt_hide = (Button) findViewById(R.id.bt_hide);
        bt_show.setOnClickListener(this);
        bt_hide.setOnClickListener(this);
      }
    
      @Override
      public void onClick(View v) {
        switch (v.getId()) {
          case R.id.bt_show:// 顯示
            try {
              View titleBar = viewStub.inflate();// 第二次加載會(huì)拋出異常
              tv_show_title = (TextView) titleBar.findViewById(R.id.tv_show_title);
              tv_show_title.setText("Title");
            } catch (Exception e) {
              viewStub.setVisibility(View.VISIBLE);
            }
            break;
          case R.id.bt_hide:// 隱藏
            viewStub.setVisibility(View.GONE);
            break;
        }
      }
    
    }
    
  4. 隱藏

    ViewStubHide
  5. 顯示

    ViewStubShow
  6. 更換加載方式

    @Override
    public void onClick(View v) {
      switch (v.getId()) {
        case R.id.bt_show:// 顯示
          viewStub.setVisibility(View.VISIBLE);// 方式二
          if (tv_show_title == null) {
            tv_show_title = (TextView) findViewById(R.id.tv_show_title);
            tv_show_title.setText("Title");
          }
          break;
      }
    }
    

04. 監(jiān)聽

  1. 加載監(jiān)聽回調(diào)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_view_stub);
      viewStub = (ViewStub) findViewById(R.id.viewStub);// 尋找控件
      // 設(shè)置加載監(jiān)聽回調(diào),成功加載后回調(diào)[只會(huì)回調(diào)一次]
      viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    
        /**
         * Listener used to receive a notification after a ViewStub has successfully inflated its layout resource.
         *
         * @param stub ViewStub 對(duì)象
         * @param inflated 被加載填充的布局
         */
        @Override
        public void onInflate(ViewStub stub, View inflated) {
          tv_show_title = (TextView) inflated.findViewById(R.id.tv_show_title);
          tv_show_title.setText("ShowTitle");
        }
      });
    }
    
  2. 按需使用

05. 源碼

  1. setVisibility() 方法

    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 {// 第一次會(huì)為空
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
          inflate();// 調(diào)用填充方法
        }
      }
    }
    
  2. 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) {// 被指定的布局的 ID
            view.setId(mInflatedId);//  即 android:inflatedId 指定的新 ID
          }
    
          final int index = parent.indexOfChild(this);// 獲取位置
          parent.removeViewInLayout(this);// 移除 ViewStub 自己
    
          final ViewGroup.LayoutParams layoutParams = getLayoutParams();// ViewStub 指定的布局參數(shù)
          if (layoutParams != null) {
            parent.addView(view, index, layoutParams);// 指定位置和參數(shù)填充
          } else {
            parent.addView(view, index);
          }
    
          mInflatedViewRef = new WeakReference<View>(view);// 弱引用
    
          if (mInflateListener != null) {// 監(jiān)聽器不空
            mInflateListener.onInflate(this, view);// 監(jiān)聽回調(diào)熙参。所以只回調(diào)一次。
          }
    
          return view;// 加載的布局返回
        } else {// 沒有指定填充的布局就拋出異常
          throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
      } else {// 父布局為空就拋出異常
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
      }
    }
    

06. 注意

  1. ViewStub 支持使用 <include> 標(biāo)簽的布局麦备。
  2. ViewStub 不支持使用 <merge> 標(biāo)簽的布局孽椰。

07. 參考

  1. Android--UI之ViewStub
  2. Android中使用ViewStub提高布局性能
  3. Android ViewStub的使用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凛篙,隨后出現(xiàn)的幾起案子黍匾,更是在濱河造成了極大的恐慌,老刑警劉巖鞋诗,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膀捷,死亡現(xiàn)場離奇詭異迈嘹,居然都是意外死亡削彬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門秀仲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來融痛,“玉大人,你說我怎么就攤上這事神僵⊙闼ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵保礼,是天一觀的道長沛励。 經(jīng)常有香客問我,道長炮障,這世上最難降的妖魔是什么目派? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮胁赢,結(jié)果婚禮上企蹭,老公的妹妹穿的比我還像新娘。我一直安慰自己智末,他們只是感情好谅摄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著系馆,像睡著了一般送漠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上由蘑,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天闽寡,我揣著相機(jī)與錄音棒厘,去河邊找鬼。 笑死下隧,一個(gè)胖子當(dāng)著我的面吹牛奢人,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淆院,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼何乎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了土辩?” 一聲冷哼從身側(cè)響起支救,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拷淘,沒想到半個(gè)月后各墨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡启涯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年贬堵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结洼。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡黎做,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出松忍,到底是詐尸還是另有隱情蒸殿,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布鸣峭,位于F島的核電站宏所,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏摊溶。R本人自食惡果不足惜爬骤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望更扁。 院中可真熱鬧盖腕,春花似錦、人聲如沸浓镜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膛薛。三九已至听隐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哄啄,已是汗流浹背雅任。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工风范, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沪么。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓硼婿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親禽车。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寇漫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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