本文針對include晴氨、merge、ViewStub三個標簽如何在布局復用碉输、有效減少布局層級以及如何可以按需加載三個方面進行介紹的籽前。
復用布局可以幫助我們創(chuàng)建一些可以重復使用的復雜布局。這種方式也意味著應用中任何在多個布局文件之間使用的通用布局都可以被提取出來,然后分別進行管理枝哄,使用的時候再進行組合肄梨。因此當我們在自定義一些View的時候,使用復用布局會更簡單方便挠锥。在平常開發(fā)中使用可以復用的布局文件众羡,不僅僅是因為它可以有效減少布局文件數(shù)量,更多的目的在于它更方面我們管理應用蓖租,布局復用粱侣,在更改某個組件時就可以做到改一個布局文件就更改了應用中所有引用該布局文件的組件,做到一改全改蓖宦。
<include/>
<include/>標簽在布局優(yōu)化中是使用最多的一個標簽了齐婴,它就是為了解決重復定義布局的問題。<include/>標簽就相當于C稠茂、C++中的include頭文件一樣柠偶,把一些常用的底層的API封裝起來,需要的時候引入即可主慰。在一些開源的J2EE中許多XML配置文件也都會使用<include/>標簽嚣州,將多個配置文件組合成為一個更為復雜的配置文件,如最常見的S2SH共螺。
在以前Android開發(fā)中该肴,由于ActionBar設計上的不統(tǒng)一以及兼容性問題,所以很多應用都自定義了一套自己的標題欄titlebar藐不。標題欄我們知道在應用的每個界面幾乎都會用到匀哄,在這里可以作為一個很好的示例來解釋<include/>標簽的使用。
下面是一個自定義的titlebar文件:
'<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/titlebar_bg"> <ImageViewandroid:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/gafricalogo"/></FrameLayout>'
在應用中使用titlebar布局文件雏蛮,我們通過<include/>標簽,布局文件如下:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/app_bg" android:gravity="center_horizontal"> <includelayout="@layout/titlebar"/> <TextViewandroid:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" android:padding="10dp"/> ... </LinearLayout>
在<include/>標簽中可以覆蓋導入的布局文件root布局的布局屬性(如layout_*屬性)涎嚼。
布局示例如下:
<includeandroid:id="@+id/news_title" android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/title"/>
如果想使用<include/>標簽覆蓋嵌入布局root布局屬性,必須同時覆蓋layout_height和layout_width屬性挑秉,否則會直接報編譯時語法錯誤法梯。
Layout parameter layout_height ignored unless layout_width is also specified on <include> tag
如果<include/>標簽已經(jīng)定義了id,而嵌入布局文件的root布局文件也定義了id犀概,<include>標簽的id會覆蓋掉嵌入布局文件root的id立哑,如果include標簽沒有定義id則會使用嵌入文件root的id。
<merge/>
<merge/>標簽都是與<include/>標簽組合使用的姻灶,它的作用就是可以有效減少View樹的層次來優(yōu)化布局铛绰。
下面通過一個簡單的示例探討一下<merge/>標簽的使用,下面是嵌套布局的layout_text.xml文件:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:text="Hello World!" android:layout_height="match_parent"/></LinearLayout>
一個線性布局中嵌套一個文本視圖产喉,主布局如下:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_wrap" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <include android:id="@+id/layout_import" android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/layout_text"/> </LinearLayout>
通過hierarchyviewer我們可以看到主布局View樹的部分層級結(jié)構(gòu)如下圖:
現(xiàn)在講嵌套布局跟布局標簽更改為<merge/>捂掰,merge_text.xml布局文件如下:
<mergexmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Hello World!"/> </merge>
然后將主布局<include/>標簽中的layout更改為merge_text.xml敢会,運行后重新截圖如下:
對比截圖就可以發(fā)現(xiàn)上面的四層結(jié)構(gòu),現(xiàn)在已經(jīng)是三層結(jié)構(gòu)了这嚣。當我們使用<merge/>標簽的時候鸥昏,系統(tǒng)會自動忽略merge層級,而把TextView直接放置與<include/>平級疤苹。
<merge/>標簽在使用的時候需要特別注意布局的類型互广,例如我的<merge/>標簽中包含的是一個LinearLayout布局視圖,布局中的元素是線性排列的卧土,如果嵌套進主布局時惫皱,include標簽父布局時FrameLayout,這種方式嵌套肯定會出問題的尤莺,merge中元素會按照FrameLayout布局方式顯示旅敷。所以在使用的時候,<merge/>標簽雖然可以減少布局層級颤霎,但是它的限制也不可小覷媳谁。
<merge/>只能作為XML布局的根標簽使用。當Inflate以<merge/>開頭的布局文件時友酱,必須指定一個父ViewGroup晴音,并且必須設定attachToRoot為true。
View android.view.LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)
root不可少缔杉,attachToRoot必須為true锤躁。
ViewStub
在開發(fā)過程中,經(jīng)常會遇到這樣一種情況或详,有些布局很復雜但是卻很少使用系羞。例如條目詳情、進度條標識或者未讀消息等霸琴,這些情況如果在一開始初始化椒振,雖然設置可見性 View.GONE ,但是在Inflate的時候View仍然會被Inflate,仍然會創(chuàng)建對象梧乘,由于這些布局又想到復雜澎迎,所以會很消耗系統(tǒng)資源。
ViewStub就是為了解決上面問題的选调,ViewStub是一個輕量級的View嗡善,它一個看不見的,不占布局位置学歧,占用資源非常小的控件。
定義ViewStub布局文件
下面是一個ViewStub布局文件:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_wrap" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ViewStub android:id="@+id/stub_image" android:layout_width="match_parent" android:layout_height="wrap_content" android:inflatedId="@+id/image_import" android:layout="@layout/layout_image"/> <ViewStub android:id="@+id/stub_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:inflatedId="@+id/text_import" android:layout="@layout/layout_text"/> </LinearLayout>
layout_image.xml文件如下(layout_text.xml類似):
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/layout_image"> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
加載ViewStub布局文件
動態(tài)加載ViewStub所包含的布局文件有兩種方式各吨,方式一使用使用inflate()方法枝笨,方式二就是使用setVisibility(View.VISIBLE)袁铐。
示例java代碼如下:
private ViewStubviewStub; protected void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_main2); viewStub = (ViewStub) findViewById(R.id.stub_image); //viewStub.inflate();//方式一 viewStub.setVisibility(View.VISIBLE);//方式二 ImageViewimageView = (ImageView) findViewById(R.id.imageView); imageView.setImageResource(R.drawable.image);}
示例View層級截圖如下:
ViewStub一旦visible/inflated,它自己就不在是View試圖層級的一部分了。所以后面無法再使用ViewStub來控制布局横浑,填充布局root布局如果有id剔桨,則會默認被android:inflatedId所設置的id取代,如果沒有設置android:inflatedId徙融,則會直接使用填充布局id洒缀。
由于ViewStub這種使用后即可就置空的策略,所以當需要在運行時不止一次的顯示和隱藏某個布局欺冀,那么ViewStub是做不到的树绩。這時就只能使用View的可見性來控制了。
layout_相關(guān)屬性與include標簽相似隐轩,如果使用應該在ViewStub上面使用饺饭,否則使用在嵌套進來布局root上面無效。
ViewStub的另一個缺點就是目前還不支持merge標簽职车。
小結(jié)
Android布局優(yōu)化基本上就設計上面include瘫俊、merge、ViewStub三個標簽的使用悴灵。在平常開發(fā)中布局推薦使用RelativeLayout扛芽,它也可以有效減少布局層級嵌套。最后了將merge和include源碼附上积瞒,ViewStub就是一個View川尖,就不貼出來了溉跃。
Include源碼
/** Exercise <include /> tag in XML files.*/public class Include extends Activity { @Override protected void onCreate(Bundleicicle) { super.onCreate(icicle); setContentView(R.layout.include_tag); }}
Merge源碼
/*** Exercise <merge /> tag in XML files.*/public class Merge extends Activity { private LinearLayoutmLayout; @Override protected void onCreate(Bundleicicle) { super.onCreate(icicle); mLayout = new LinearLayout(this); mLayout.setOrientation(LinearLayout.VERTICAL); LayoutInflater.from(this).inflate(R.layout.merge_tag, mLayout); setContentView(mLayout); } public ViewGroupgetLayout() { return mLayout; }}