編輯:關於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、減少視圖層級
前言在應用程序開發過程中,經常會采用webview來展現某些界面,這樣就可以不受發布版本控制,實時更新,遇到問題可以快速修復。但是在Android開發中,由於Androi
最近做安卓項目中使用到了百度地圖的API,在申請百度地圖key的時候,需要我們填入“簽名的SHA1”和“客戶端包名”,然後百度為我們生成一個key。於是就引發了思考,百度
在軟件開發的過程中,為了讓軟件在不同的場景下都可以使用,所以機型適配是不可或缺並且非常重要耗時的一個環節。一:機型適配需要考慮的幾個方面:1,Android的版本2.手機
在前一期中,我們做了懸浮頭部的兩個tab切換和下拉刷新效果,後來項目中要求改成三個tab,當時就能估量了一下,如果從之前的改,也不是不可以,但是要互相記住的狀態就太多了,