編輯:關於Android編程
最近在研究開發一些基於Android的App,遇到了一些問題,其中一個比較關鍵的是在Activity中的onCreate()方法中獲取Button對象,代碼大概如下:
private Button mTrueButton;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
mTrueButton = (Button) getViewById(R.id.true_button);
mTrueButton.setOnClickListener(…);
}
該代碼是根據一本android的編程指南進行修改的,但意想不到的是,一執行,app提示系統錯誤,終止運行,通過debug跟蹤發現:mTrueButton為null,系統執行到mTrueButton.setOnclickListener拋出java.lang.NullPointerException,很明顯,此時調用getViewById無法獲取到Button這個View對象,於是開始網上搜索資料,認為可能的原因是下面幾個:
一,調用順序不當導致的異常
持這種觀點的主要原因是getViewById的調用放到了setContentView之前,如下:
super.onCreate(savedInstanceState);
mTrueButton = (Button)getViewById(R.id.true_button);
setContentView(R.layout.activity_quiz);
理由是:當activity 調用 setContentView() 時,android 才會去繪制 layout 上的各個元素,並為其分配內存。只有 分配了內存以後,才能繼續執行 ,findViewById(); 才能得到引用,不然得到空引用,空引用意味著,後面使用相應變量時就會發生訪問的對象不存在的問題。
而且當Activity重新setContentView()以後,那些之前繪制的控件,內存都被滅掉了。
所以,若是通過setContentView 來達到畫面切換目的的,要注意重新繪制以後重新取得引用
二,getViewById的上下文對象不匹配
這種方式讓筆者想到Javascript中的document.getElementById,兩者具有非常高的相似性,getElementById的調用需要指定對應的document對象,表示從該document對象獲取元素,同理,Android中的getViewById的完整調用是View.getViewById,因此需要關注該方法默認的context對象,一般是this,即當前的Activity,但有時候可能不是這樣,如:
userDialog=new Dialog(addevent.this);
userDialog.setContentView(R.layout.user_list);
userDialog.setTitle("請選擇");
ListView lv=(ListView)userDialog.findViewById(R.id.userList);
lv.setAdapter(new MyAdapter());
userDialog.show();
如上,實例化lv時必須指定userDialog.findViewById()而不能直接findViewById(),否則就會從Activity而不是Dialog的布局文件中找R.id.userList,此時當然會返回null,執行lv.setAdapter(new MyAdapter());時就會出現NullPointException異常
三,開發工具Eclipse導致的問題
假定在自定的Adapter的getView方法中有類似如下的代碼:
View rowview = (View)inflater.inflate(R.layout.rowview, parent, false);
TextView tv_contact_id =(TextView)rowview.findViewById(R.id.tv_contact_id);
TextView tv_contactname =(TextView)rowview.findViewById(R.id.tv_contactname);
有時候居然也會發現rowview非空,但tv_contact_id和tv_contactname都是null!仔細看代碼,怎麼也看不出錯誤來。到底是什麼原因造成的呢?答案是Eclipse造成的,要解決這個問題,需要這個項目clean一次(Project菜單 -> Clean子菜單),這樣就OK了。
四,新版本SDK不能在onCreate方法中調用了-筆者的問題原因在此。
重要的環境交代:剛學Android,在官網下載的新版的ADT以及新版的SDK在新版的IDE(ADT)創建項目時如果你的最小版本(minimumrequiredSDK)要支持4.0以下版,並且目標版本為(4.0+),那麼此時IDE會為你創建一個兼容包(appcompat_v7)創建項目後,這個時候在生成的項目主Activity不是以前的那種繼承的Activity,而是繼承的ActionBarActivity。
此時,如果你仍然用舊的辦法在onCreate調用getViewById,那麼會返回null,原因是:在新的layout文件不是存放在默認的(res/layout/activity_quiz.xml)文件中,而是存放在(res/layout/fragment_quiz.xml)文件中。所以要在fragment_quiz.xml去找對應的ID才會找到,而新的IDE生成的代碼中加載(fragment_quiz.xml)文件是在一個內部類加載的,所以一種方法是:我們可以在內部類加載處來得到Button。
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
View rootView = null;
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_quiz, container, false);
mTrueButton = (Button) rootView.findViewById(R.id.true_button);
System.out.println(button);
return rootView;
}
}
另外一種方法是:如果熟悉Activity的生命周期的人可以知道:onCreate調用的時候其實還沒構造對應的布局對象,因此不能在onCreate函數中獲取控件,但可以在onStart函數中獲取:(筆者的方案就是在onStart方法中獲取的)
@Override
protected void onStart() {
super.onStart();
mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new android.view.View.OnClickListener(){
public void onClick(android.view.View v) {
//TODO...
}
});
}
繼續剛剛的講,完成開發環境的搭配之後,我們就可以開始自己開發自己的應用程序了。 1、先熟悉一下整個開發環境的目錄結構。PS:至於eclipse的使用在這裡就不多說了,
前言 在微信剛流行的時候,在搖一搖還能用來那啥的時候,我也曾深更半夜的拿著手機晃一晃。當時想的最多的就是,我靠,為神馬搖一下需要用這麼大的力度,當時我想可能騰訊覺
最近編程時,發現一個針對HashMap的一個提示:翻譯過來就是:用SparseArray來代替會有更好性能。那我們就來看看源碼中SparseArray到底做了哪些事情:一
前言筆者最近離職找工作快兩周了,這段時間陸陸續續也見識了北上廣這邊跟西部城市對待技術理念的差異和學習深度.俗話說:知恥而後勇,在經歷了面試被虐得體無完膚的過程後,我也找到