对于 android 自定义视图构造函数,我应该调用 super() 还是调用 this() ?

发布于 2025-01-03 06:19:31 字数 1136 浏览 1 评论 0原文

创建自定义视图时,我注意到很多人似乎都是这样做的:

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 和代码很好地创建视图,但我不确定这种方法是否有任何缺点。这在所有情况下都有效吗?

这个问题的另一部分

When creating a custom view, I have noticed that many people seem to do it like this:

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.
}

My problem with this is that it stops me from making my variables final. Any reason not to do the following?

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
}

I've been able to create the view just fine through XML and through code, but I'm not sure if there are any drawbacks to this approach. Will this work in all cases?

There is another part to this question

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

森林迷了鹿 2025-01-10 06:19:32

编辑:

这不行。查看此问题的其他答案以了解原因。


原答案:

可以。

当我们查看 TextView.java

他们使用相同的层次结构。
所以你可以接受这种方法。

Edit:

This is not Okay. See other answers to this question for reasons.


Original Answer:

It is Ok.

When we look at the source of TextView.java.

They have used the same hierarchy.
So you are Okay with this approach.

独守阴晴ぅ圆缺 2025-01-10 06:19:31

我看到的唯一缺点(似乎没有人提到)是你的第二个构造函数丢失了超类的 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);
}

但这并不完全是“纯粹”的方式,因为你人为地强迫它具有相同的 defStyle< /代码> 作为列表视图。

因此,与其他人所说的相反,我实际上认为您最好使用帖子中概述的第一个 doAdditionalConstructorWork() 方法,因为这至少可以确保 defStyle 设置正确。

The only drawback I can see (that no one seems to have mentioned) is that your second constructor loses the defStyle of the superclass, because you set it to zero. Look at the source code for any of Android's View classes, and you'll notice that the second constructor always has a specific defStyle defined.

For example, this is the second constructor of ListView:

public ListView(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.listViewStyle);
}

If you were to extend ListView using the second approach that you describe, com.android.internal.R.attr.listViewStyle would no longer be the defStyle, because you'd be bypassing that second super constructor and making it zero instead. I suppose you could resolve this by using the same defstyle as ListView, like so:

public MyView(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.listViewStyle);
}

But it's not exactly the "purist" way, because you're artificially forcing it to have the same defStyle as ListView.

So, contrary to what the others said, I actually think you're better off using the first doAdditionalConstructorWork() approach outlined in your post, because that at least makes sure that the defStyle is set correctly.

不美如何 2025-01-10 06:19:31

从我对类似问题的回答中复制了此内容。

如果您重写所有三个构造函数,请不要级联 this(...) 调用。相反,您应该这样做:

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 作为样式属性。

Copied this from my answer for a similar question.

If you override all three constructors, please DO NOT CASCADE this(...) CALLS. You should instead be doing this:

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
}

The reason is that the parent class might include default attributes in its own constructors that you might be accidentally overriding. For example, this is the constructor for 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);
}

If you did not call super(context), you would not have properly set R.attr.textViewStyle as the style attr.

音栖息无 2025-01-10 06:19:31

是的,这是一种合理的使用模式,因此您不必在每个构造函数中重复自定义工作。不,该方法似乎没有任何缺点。

Yup, that's a reasonable pattern to use so you don't have to repeat the custom work in every one of your constructors. And no, there don't appear to be any drawbacks to the method.

悸初 2025-01-10 06:19:31

这完全取决于您的要求。假设您想使用父类中的任何方法而不覆盖自定义视图中的功能,那么您需要使用 super() 并实例化父类。如果您不需要调用父类中的任何方法,那么您就不需要在自定义视图中重写所有实现。阅读此中的自定义视图示例部分链接

It purely depends on your requirement. Let us say if you want to use any methods in parent class without overriding their functionality in your custom view, then you need to use super() and instantiate parent class. If you dont need to invoke any methods in parent class all implementations are overridden in your custom view, then you don't need. Read A custom View Example section in this link.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文