Android-布局優(yōu)化merge, viewStub, include總結(jié)

多層布局的嵌套會(huì)導(dǎo)致頁(yè)面加載慢,影響用戶(hù)的體驗(yàn),今天我們就來(lái)學(xué)學(xué)如何使用 include,merge及viewStub。

1.include

include便于對(duì)相同視圖內(nèi)容進(jìn)行統(tǒng)一的控制管理,提高布局重用性,以標(biāo)題欄為例,我們先定義一個(gè)通用的標(biāo)題欄,相關(guān)代碼如下:
commont_title

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_commontitle_root"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:background="#0951C1">

    <TextView
        android:id="@+id/tv_back_commontitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="3dp"
        android:drawableLeft="@mipmap/back"
        android:drawablePadding="3dp"
        android:gravity="center_vertical"
        android:layout_centerVertical="true"
        android:minHeight="50dp"
        android:text="返回"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_title_commontitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="標(biāo)題"
        android:textSize="18sp" />

</RelativeLayout>

然后在我們的MainActivity頁(yè)面引入,我們的MainActivity頁(yè)面有一個(gè)加載視圖的按鈕

<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="match_parent"
    tools:context=".MainActivity">

    <include
        android:id="@+id/iclude_main"
        layout="@layout/common_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
   />

    <Button
        android:id="@+id/btn_main_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="加載視圖"/>

</RelativeLayout>

效果如下:


image.png

那我們?nèi)绻朐O(shè)置標(biāo)題怎么辦?當(dāng)然是findviewbyid()然后set了,如下:

         RelativeLayout relativeLayout = findViewById(R.id.ll_commontitle_root);
        TextView titleTv =  relativeLayout.findViewById(R.id.tv_title_commontitle);
        titleTv.setText("主界面");

運(yùn)行,我擦,報(bào)錯(cuò)了。

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.widget.RelativeLayout.findViewById(int)' on a null object reference

    at com.hxzk.layoutoptimizeproject.MainActivity.onCreate(MainActivity.java:17)

不能找到這個(gè)id,獲取的RelativeLayout對(duì)象為null,這是為何?原來(lái):如果給include設(shè)置了id,就會(huì)覆蓋掉引用布局根布局的id,所以解決辦法用兩種:

  • 第一種直接獲取include的id,進(jìn)行findviewByid()
  • 第二種將兩者的id取名一致

我們選取第一種,結(jié)果如下:


image.png

2.merge

merge標(biāo)簽是作為include標(biāo)簽的一種輔助擴(kuò)展來(lái)使用的,也就是需要和include一起使用,它的主要作用是為了防止在引用布局文件時(shí)產(chǎn)生多余的布局嵌套。我們先看看我們現(xiàn)在的視圖層級(jí)(通過(guò)android studio自帶的Layout inspector):

image.png

歐克,我們看看我們將include中的布局改為merge,注意:merge必須放在布局文件的根節(jié)點(diǎn)上。這里做一個(gè)說(shuō)明如果將RelativeLayout改為merge,Releative中所有的屬性將都無(wú)法使用,因?yàn)閙erge不是一個(gè)view,merge extends Activity,所以我們直接刪除相關(guān)屬性

<merge
    xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/tv_back_commontitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="3dp"
        android:drawableLeft="@mipmap/back"
        android:gravity="center"
        android:text="返回"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/tv_title_commontitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="標(biāo)題"
        android:textSize="18sp" />

</merge>

activity_main.xml中:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_main_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

    <Button
        android:id="@+id/btn_main_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="加載視圖"/>

</RelativeLayout>

MainActivity中(第一種寫(xiě)法):

       TextView titleTv =  findViewById(R.id.tv_title_commontitle);
        titleTv.setText("主界面");

其實(shí)還有一種寫(xiě)法是不在xml中通過(guò)include引入,而是通過(guò)代碼直接引入merge:
我們給activity_main.xml的根Relative設(shè)置id為 android:id="@+id/rl_main_root",在通過(guò)LayoutInflate.inflate方法渲染的時(shí)候, 第二個(gè)參數(shù)必須指定一個(gè)父容器,且第三個(gè)參數(shù)必須為true,也就是必須為merge下的視圖指定一個(gè)父親節(jié)

       RelativeLayout parentRl = findViewById(R.id.rl_main_root);
        LayoutInflater.from(this).inflate(R.layout.common_title,parentRl,true);
        TextView titleTv =  findViewById(R.id.tv_title_commontitle);
        titleTv.setText("主界面");

結(jié)果:


image.png

運(yùn)行后再查看一下視圖層級(jí):

image.png

merge的使用,相當(dāng)于直接將原RelativeLayout中的控件搬運(yùn)到了父RelativeLayout中,所以merge所包含的控件之前的位置屬性啥的要做響應(yīng)的調(diào)整,對(duì)于父RelativeLayout。

2.1merge的優(yōu)缺點(diǎn)

通過(guò)上面的代碼及效果我們可以明顯的看的優(yōu)缺點(diǎn)。

2.1.1merge的優(yōu)點(diǎn)

  • 減少了層級(jí)的嵌套,提高了渲染的效率。

2.1.2merge的缺點(diǎn)

缺點(diǎn)也是比較明顯:

  • 由于merge不是view.原ViewGroup的屬性都失效(對(duì)merge標(biāo)簽設(shè)置的所有屬性都是無(wú)效的),也就是背景色啥的都不能正常顯示。

3.ViewStub

ViewStub有點(diǎn)類(lèi)似于懶加載,就是什么時(shí)候需要加載相關(guān)視圖了,在做顯示。話不多說(shuō),上代碼:

  <Button
        android:id="@+id/btn_main_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       android:layout_centerInParent="true"
        android:text="加載視圖"/>

    <ViewStub
        android:id="@+id/vs_main"
        android:inflatedId="@+id/vs_main_inflatedId"
        android:layout="@layout/vs_layout"
        android:layout_below="@id/btn_main_next"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

其中inflatedId是要加載的layout根布局的ViewGroup的id,layout是要加載的布局。其余屬性不多說(shuō)。
vs_layout布局:

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

    <Button
        android:id="@+id/btn_vscontent_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="viewStub中的內(nèi)容0"
        android:layout_gravity="center"
        />
    <Button
        android:id="@+id/btn_vscontent_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="viewStub中的內(nèi)容1"
        android:layout_gravity="center"
        />
    <Button
        android:id="@+id/btn_vscontent_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="viewStub中的內(nèi)容2"
        android:layout_gravity="center"
        />

</LinearLayout>

MainActivity中的代碼:

  Button btnNext =findViewById(R.id.btn_main_next);
        btnNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 這里調(diào)用的是inflate方法,當(dāng)然,也可以調(diào)用setVisibility方法(但是不建議這么做)
                // 只能點(diǎn)擊一次加載視圖按鈕,因?yàn)閕nflate只能被調(diào)用一次。調(diào)用完成ViewStub被銷(xiāo)毀
                // 如果再次點(diǎn)擊按鈕,會(huì)拋出異常"ViewStub must have a non-null ViewGroup viewParent"
                ViewStub viewStub = findViewById(R.id.vs_main);
                if(viewStub != null){
                    viewStub.inflate();
                    //這里注意ViewStub只是一個(gè)容器,所以在其顯示后,其中的view就是在Activity中展示,所以直接findViewByid即可
                    Button btnOne =findViewById(R.id.btn_vscontent_one);
                    Button btnTwo =findViewById(R.id.btn_vscontent_two);
                    btnOne.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Toast.makeText(MainActivity.this,"點(diǎn)擊了第一個(gè)ViewStub按鈕",Toast.LENGTH_LONG).show();
                        }
                    });
                    btnTwo.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Toast.makeText(MainActivity.this,"點(diǎn)擊了第二個(gè)ViewStub按鈕",Toast.LENGTH_LONG).show();

                        }
                    });
                }
            }
        });

我們獲取了ViewStub內(nèi)容沒(méi)有加載的布局層級(jí):


image.png

ViewStub內(nèi)容已加載的布局層級(jí):


image.png

ViewStub標(biāo)簽使用注意點(diǎn):
1,ViewStub標(biāo)簽不支持merge標(biāo)簽(ViewStub的加載布局中不能有merge,但merge中可以有ViewStub)。因此這有可能導(dǎo)致加載出來(lái)的布局存在著多余的嵌套結(jié)構(gòu),開(kāi)發(fā)中視情況而定。
2,ViewStub的inflate只能被調(diào)用一次,第二次調(diào)用會(huì)拋出異常。
3,雖然ViewStub是不占用任何空間的,但是每個(gè)布局都必須要指定layout_width和layout_height屬性,否則運(yùn)行就會(huì)報(bào)錯(cuò)。

完畢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容