我是否需要Android自定义视图的所有三个构造函数?

时间:2012-02-08 14:55:52

标签: android android-custom-view

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

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

我的第一个问题是,构造函数MyView(Context context, AttributeSet attrs, int defStyle)怎么样?我不确定它在哪里使用,但我在超级课程中看到它。我是否需要它,它在哪里使用?

another part to this question

6 个答案:

答案 0 :(得分:136)

如果您要从View添加自定义xml,请执行以下操作:

 <com.mypack.MyView
      ...
      />

您将需要构造函数public MyView(Context context, AttributeSet attrs),否则当Android尝试让Exception充气时,您将获得View

如果您从View添加xml,并指定android:style属性,请执行以下操作:

 <com.mypack.MyView
      style="@styles/MyCustomStyle"
      ...
      />

在应用显式XML属性之前,还将调用第二个构造函数并将样式默认为MyCustomStyle

当您希望应用程序中的所有视图具有相同的样式时,通常会使用第三个构造函数。

答案 1 :(得分:111)

如果覆盖所有三个构造函数,请不要CASCADE 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 } CALLS。你应该这样做:

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,则无法将{{1}}正确设置为样式attr。

答案 2 :(得分:42)

MyView(上下文上下文)

以编程方式显示视图时使用。

MyView(上下文上下文,AttributeSet attrs)

LayoutInflater用于应用xml属性。如果其中一个属性名为style,则在布局xml文件中查找显式值之前,将查找属性。

MyView(Context context,AttributeSet attrs,int defStyleAttr)

假设您要将默认样式应用于所有窗口小部件,而不必在每个布局文件中指定style。例如,默认情况下使所有复选框变为粉红色。您可以使用defStyleAttr执行此操作,框架将在您的主题中查找默认样式。

请注意,前一段时间defStyleAttr被错误地命名为defStyle,并且对是否真的需要此构造函数进行了一些讨论。见https://code.google.com/p/android/issues/detail?id=12683

MyView(Context context,AttributeSet attrs,int defStyleAttr,int defStyleRes)

如果您可以控制应用程序的基本主题,则第3个构造函数可以正常工作。这适用于谷歌,因为他们将他们的小部件与默认主题一起发送。但是假设您正在编写小部件库,并且您希望设置默认样式而无需用户调整其主题。您现在可以使用defStyleRes通过将其设置为2个第一个构造函数中的默认值来执行此操作:

public MyView(Context context) {
  super(context, null, 0, R.style.MyViewStyle);
  init();
}

public MyView(Context context, AttributeSet attrs) {
  super(context, attrs, 0, R.style.MyViewStyle);
  init();
}

总而言之

如果您正在实现自己的视图,则只需要2个第一个构造函数,并且可以由框架调用。

如果您希望您的视图是可扩展的,那么您可以为您的类的子级实现第4个构造函数,以便能够使用全局样式。

我没有看到第3个构造函数的真实用例。如果您没有为您的小部件提供默认样式但仍希望您的用户能够这样做,则可能是一种快捷方式。不应该发生那么多。

答案 3 :(得分:3)

Kotlin似乎带走了很多痛苦:

class MyView
@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
    : View(context, attrs, defStyle)

@JvmOverloads将生成所有必需的构造函数(请参阅注释的documentation),每个构造函数都可能调用super()。然后,只需用Kotlin init {}块替换初始化方法。 Boilerplate代码消失了!

答案 4 :(得分:0)

第三个构造函数要复杂得多。让我举个例子。

Support-v7 SwitchCompact软件包支持thumbTinttrackTint属性,因为24版本而23版本不支持它们。现在你想在23版本中支持它们,你将如何做?实现这个目标?

我们假设使用自定义视图SupportedSwitchCompact extends SwitchCompact

public SupportedSwitchCompat(Context context) {
    this(context, null);
}

public SupportedSwitchCompat(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public SupportedSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init(){
    mThumbDrawable = getThumbDrawable();
    mTrackDrawable = getTrackDrawable();
    applyTint();
}

这是一种传统的代码风格。请注意,我们将0传递给第三个参数。当你运行代码时,你会发现getThumbDrawable()总是返回null有多奇怪,因为方法getThumbDrawable()是它的超级SwitchCompact方法。

如果你将R.attr.switchStyle传递给第三个参数,一切顺利。为什么?

第三个参数是一个简单的属性。该属性指向样式资源。在上述情况下,系统会在当前主题中找到switchStyle属性,幸运的是系统找到它。

frameworks/base/core/res/res/values/themes.xml中,您会看到:

<style name="Theme">
    <item name="switchStyle">@style/Widget.CompoundButton.Switch</item>
</style>

答案 5 :(得分:-2)

如果你必须包括三个类似现在讨论的构造函数,你也可以这样做。

public MyView(Context context) {
  this(context,null,0);
}

public MyView(Context context, AttributeSet attrs) {
  this(context,attrs,0);
}

public MyView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  doAdditionalConstructorWork();

}