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");
}
}
- 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.
- 在xml 中定義ViewStub 節(jié)點時啥容,內(nèi)部不能包含其他節(jié)點,也就是說顷霹,ViewStub 是一個自閉合節(jié)點咪惠,如果一個布局/view如果想通過ViewStub顯示,只能定義在單獨的xml 文件中淋淀。