一.自定義控件 findViewById返回為null
首先講一個具體的問題,這幾天在做demo時,寫了一個自定義組合控件,最后在run的時候顯示這兩行報錯。原先還以為是setOnClickListener錯了,后來經過debug才發現findViewById查找我的自定義組合控件為null !

debug結果:

接下來就開始了我痛苦的找bug過程,關於這段血淚過程,來總結一下findViewById 返回為空的出錯原因。
首先回憶一下如何寫一個自定義組合控件:
- 將組合控件的布局,抽取到單獨的一個xml中
- 通過一個單獨的類,去加載此段布局文件.
步驟並不復雜,可是這里卻有三個出錯點!
1. 當你在使用自定義的組合控件時,在xml文件中使用該控件時,不能簡單的寫類名,包名也要!
<com.gym.mobile.view.SettingItemView android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content"/>
而不能簡單的寫一個:
<SettingItemView android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content"/>
2.【重點!】我們再寫單獨類時,必定會繼承某個類,要繼承它的構造方法,一定要注意,下面講解一下這3個構造方法:
//使用在java代碼創建控件(無法加載XML文件中定義的控件屬性) public FocusTextView(Context context) { super(context); } //由系統調用(上下文環境構造方法 + 帶屬性) public FocusTextView(Context context, AttributeSet attrs) { super(context, attrs); } //由系統調用(上下文環境構造方法 + 帶屬性 + 布局文件中定義樣式文件構造方法) public FocusTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
這里繼承的3種構造方法及super調用父類一定要仔細對應!大部分錯誤都是出現在這里,而且在寫自定義控件如果涉及到自定義屬性時,一定要繼承第二個構造方法!還涉及到樣式,則第三個構造方法也要寫!!
(而我的demo錯誤就是在寫構造方法時,super調用父類構造函數時對應的參數有誤,導致findViewById 返回為null,花了好長時間 :(
3.如果還運用到自定義屬性的話,一定要在運用屬性的控件內添加該項目的xmlns
xmlns:mobilesafe="http://schemas.android.com/apk/res/com.gym.mobile"
<com.gym.mobile.view.SettingItemView xmlns:mobilesafe="http://schemas.android.com/apk/res/com.gym.mobile" android:id="@+id/siv_update" android:layout_width="match_parent" android:layout_height="wrap_content" mobilesafe:destitle="自動更新設置" mobilesafe:desoff="自動更新已關閉" mobilesafe:deson="自動更新已開啟"> </com.itheima.mobilesafe74.view.SettingItemView>
二. findViewById 返回為null
以上討論的是一個特殊情況即自定義控件時,下面從整體分析findViewById 返回為null的情況,我們先好好思考findViewById的使用:
findViewById的完整寫法是View.findViewById(),而不指定View時默認的是Context,因此當findViewById不是在context里執行時,要指定對應的View! 實例化控件時必須指定XXX.findViewById()而不能直接findViewById(),否則就會從Activity而不是特定的某個布局文件中找R.id.XXX 當然,如果findviewbuid之前加載了對應的布局,即可不必在findViewById之前寫對應的view !!!
關於以上這段話,也有幾個出錯點!
1.可能性最大的一種,也是很粗心的一種,你在加載視圖的操作之前使用了findViewById尋找控件Id !試問視圖都沒有加載出來,控件id是找不到的。
試圖加載即:
setContentView(R.layout.activity_splash);
(以下為錯誤示范。。。)

2.還有一種可能性,錯誤很隱蔽!也是關於加載視圖的問題,在尋找控件時,控件所處的xml文件要與加載的視圖相同,否則setContentView中加載的視圖與你需要尋找控件所處的視圖不同,兩個毫不相關的視圖,怎么聯系到一塊?所以跟一開始思考findViewById那段話一樣,你需要在使用findViewById之前加上相應控件所處的視圖!
【!!!】最典型的情況就是你在使用了 inflate將特定的xml轉換成view之后,再使用findViewById找view里的控件是找不到的!,因為它這里默認的是 this.findViewById(R.id.bt_submit),
所以你需要將其改為view.findViewById(R.id.bt_submit)
錯誤示范:

正確改法!:

