編輯:關於Android編程
在項目中,難免會遇到這種需求,在程序運行時需要動態根據條件來決定顯示哪個View或某個布局,最通常的想法就是把需要動態顯示的View都先寫在布局中,然後把它們的可見性設為View.GONE,最後在代碼中通過控制View.VISIABLE動態的更改它的可見性。這樣的做法的優點是邏輯簡單而且控制起來比較靈活。但是它的缺點就是,耗費資源,雖然把View的初始可見View.GONE但是在Inflate布局的時候View仍然會被Inflate,也就是說仍然會創建對象,會被實例化,會被設置屬性。也就是說,會耗費內存等資源。
推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,使用非常簡單:
mViewStub = (ViewStub) this.findViewById(R.id.viewstub);
mViewStub.inflate();
它一個不可見的,不占布局位置,占用資源非常小的控件,相當於一個“占位控件”。使用時可以為ViewStub指定一個布局,在Inflate布局的時候,只有ViewStub會被初始化,然後當ViewStub被設置為可見的時或調用了ViewStub.inflate()的時候,ViewStub所指向的布局就會被inflate實例化,且此布局文件直接將當前ViewStub替換掉,然後ViewStub的布局屬性(layout_margin***、layout_width等)都會傳給它所指向的布局。這樣,就可以使用ViewStub在運行時動態顯示布局,節約內存資源。
下面我們從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);
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);
}
}
我們先從方法的入口開始看:
1、在第2行,首先是得到ViewStub它的父視圖對象。
2、然後在第4行一開始肯定是能進入判斷的,mLayoutResource就是需要inflate的布局資源,然後在第13行填充這個布局資源。
3、然後在第21行,重要的來了,parent.removeViewInLayout(this);這段代碼是什麼意思呢?看方法名字就知道了,this是代表ViewStub對象,意思就是把當前ViewStub對象從父視圖中移除了。
4、然後第23~28行,就是得到ViewStub的LayoutParams布局參數對象,如果存在就把它賦給被inflate的布局對象,然後把inflate的布局對象添加到父視圖中。
5、最後返回inflate的布局對象。
從上述可知,當我們第二次調用ViewStub.inflate()方法的時候,因為已經移除了ViewStub對象,在第2、4行,得到的viewParent就為null,此時判斷時候就會走else拋出一個IllegalStateException異常:ViewStub must have a non-null ViewGroup viewParent。
需要注意的幾點:
1.ViewStub之所以常稱之為“延遲化加載”,是因為在教多數情況下,程序無需顯示ViewStub所指向的布局文件,只有在特定的某些較少條件下,此時ViewStub所指向的布局文件才需要被inflate,且此布局文件直接將當前ViewStub替換掉,具體是通過viewStub.infalte()或viewStub.setVisibility(View.VISIBLE)來完成。
2.正確把握住ViewStub的應用場景非常重要,因為使用ViewStub可以優化布局,一般應用在當前布局或控件在用戶使用較少情況下,這樣可以提高性能,節約內存,加快界面渲染。
3.對ViewStub的inflate操作只能進行一次,因為inflate的時候是將它指向的布局實例化並替換掉當前ViewStub本身(由此體現出了ViewStub“占位”性質),一旦替換後,此時原來的布局文件中就沒有ViewStub控件了,因此,如果多次對ViewStub進行infalte,會出現錯誤信息:ViewStub must have a non-null ViewGroup viewParent。
4.3中所講到的ViewStub指向的布局文件解析inflate並替換掉當前ViewStub本身,並不是完全意義上的替換(與include標簽不太一樣),替換時,布局文件的layout params是以ViewStub為准,其他布局屬性是以布局文件自身為准。
5.ViewStub本身是不可見的,對ViewStub.setVisibility(int visibility)與其他View控件不一樣,我們可以從源碼角度來看一下ViewStub.setVisibility()方法的作用:
這個方法意思就是ViewStub的setVisibility()設置成View.VISIBLE或INVISIBLE如果是首次使用,都會自動inflate其指向的布局文件,並替換ViewStub本身,再次使用則是相當於對其指向的布局文件設置可見性。
好了,原理講了那麼多,來看看代碼怎麼實現吧:
首先看看效果圖:

使用了ViewStub的activity_main.xml:
hide_layout.xml
代碼文件:
public class MainActivity extends ActionBarActivity {
private ViewStub mViewStub;
private Switch mSwitch;
private boolean flag = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewStub = (ViewStub) this.findViewById(R.id.viewstub);//實例化ViewStub
mSwitch = (Switch) findViewById(R.id.switch1);
mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked) {
if(!flag){
mViewStub.inflate();//ViewStub只能被inflate一次,會返回一個填充資源的View對象
//mViewStub.setVisibility(View.VISIBLE);)
flag = true;
}else{
mViewStub.setVisibility(View.VISIBLE);
}
Button mBtn = (Button) findViewById(R.id.hide_layout_btn);//ViewStub被替換的布局內的控件
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), Click me!,
Toast.LENGTH_SHORT).show();
}
});
} else {
mViewStub.setVisibility(View.GONE);
}
}
});
}
}
注:使用ViewStub被替換的布局中的控件,直接findViewById即可。
1、布局重用
使用include標簽中布局文件中的控件,直接findViewById即可。
2、減少視圖層級
Android WebView填坑記錄
前言在應用程序開發過程中,經常會采用webview來展現某些界面,這樣就可以不受發布版本控制,實時更新,遇到問題可以快速修復。但是在Android開發中,由於Androi
android 關於利用簽名的SHA1進行安全校驗的方法之一(推薦)
最近做安卓項目中使用到了百度地圖的API,在申請百度地圖key的時候,需要我們填入“簽名的SHA1”和“客戶端包名”,然後百度為我們生成一個key。於是就引發了思考,百度
Android之機型適配
在軟件開發的過程中,為了讓軟件在不同的場景下都可以使用,所以機型適配是不可或缺並且非常重要耗時的一個環節。一:機型適配需要考慮的幾個方面:1,Android的版本2.手機
Android多個TAB選項卡切換效果
在前一期中,我們做了懸浮頭部的兩個tab切換和下拉刷新效果,後來項目中要求改成三個tab,當時就能估量了一下,如果從之前的改,也不是不可以,但是要互相記住的狀態就太多了,