自定义View的裁坑实录–构造函数的调用导致NPE
问题场景:自定义View在创建的时候,会要求重写四个带参构造函数。代码如下:
public class test extends View {
public test(Context context) {
super(context);
}
public test(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public test(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public test(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
一定要注意,现在每个构造函数的都是在调用super(父类)的构造函数。
此外,根据项目需求,一般会需要重写onAttachedToWindow()函数,这个函数是view在绑到窗口的时候调用的。上下文注册广播的时候,可以在这里重写。我是将一些更新逻辑写在这个里面。
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
然后,我在第三个构造函数,开始初始化一些属性。在layout中写入自己定义的view后,编译、安装、运行,很丝滑嗷,也顺便很丝滑的NPE(NullPointerException)了。擦,开始排查。在onAttached函数里大了日志。发现onAttachedToWindow函数调用比构造函数还早----这是错误的。但是我还真信了。开始查为什么会这样,gpt一问,发现是先调用构造函数,那就说明我构造函数调用的问题。通过打日志,发现在创建的时候,构造函数调用的并不是第三个(带三个参数的)。这时候,回过头才发现,其他几个构造函数的调用是super,根本没有初始化我的属性。到此真相大白,将构造函数的super改成this的调用。就可以初始化了。
public test(Context context) {
this(context, null, 0);
}
public test(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public test(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public test(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
总结
作为一名优秀的复制粘贴工程师,一定要记得抄代码的时候要仔细、仔细、在仔细,否则一定会是一段血泪史。