在创建自定义视图时,我注意到许多人似乎这样做:
public MyView(Context context) {
super(context);
// this constructor used when programmatically creating view
doAdditionalConstructorWork();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// this constructor used when creating view through XML
doAdditionalConstructorWork();
}
private void doAdditionalConstructorWork() {
// init variables etc.
}
我的问题在于它阻止我使我的变量最终。有什么理由不做以下事情?
public MyView(Context context) {
this(context, null);
// this constructor used when programmatically creating view
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// this constructor used when creating view through XML
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// this constructor used where?
// init variables
}
我已经能够通过XML和代码创建视图,但我不确定这种方法是否有任何缺点。这会在所有情况下都有效吗?
我能看到的唯一缺点(似乎没有人提到)是你的第二个构造函数丢失了超类的defStyle
,因为你将它设置为零。查看任何Android的View类的源代码,您会注意到第二个构造函数始终定义了特定的defStyle
。
例如,这是ListView的第二个构造函数:
public ListView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.listViewStyle);
}
如果您使用您描述的第二种方法扩展ListView,com.android.internal.R.attr.listViewStyle
将不再是defStyle
,因为您将绕过第二个超级构造函数并将其改为零。我想你可以通过使用与ListView相同的defstyle
来解决这个问题,如下所示:
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.listViewStyle);
}
但它并不完全是“纯粹主义”的方式,因为你人为地强迫它拥有与ListView相同的defStyle
。
所以,与其他人所说的相反,我实际上认为你最好使用你帖子中概述的第一个doAdditionalConstructorWork()
方法,因为这至少可以确保正确设置defStyle
。
从我对类似问题的答案中复制了这个。
如果你覆盖所有三个构造函数,请不要CASCADE this(...)
CALLS。你应该这样做:
public MyView(Context context) {
super(context);
init(context, null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context,attrs);
init(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
// do additional work
}
原因是父类可能在其自己的构造函数中包含您可能意外覆盖的默认属性。例如,这是TextView
的构造函数:
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
如果你没有调用super(context)
,你就不会将R.attr.textViewStyle
正确设置为样式attr。
是的,这是一个合理的模式,因此您不必在每个构造函数中重复自定义工作。不,该方法似乎没有任何缺点。
它完全取决于您的要求。让我们说如果你想在父类中使用任何方法而不在自定义视图中覆盖它们的功能,那么你需要使用super()和实例化父类。如果您不需要在父类中调用任何方法,则在自定义视图中覆盖所有实现,然后您不需要。阅读此link中的自定义视图示例部分。