一.自定义控件 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)
错误示范:
正确改法!: