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

Mic*_*ine 136 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.
}
Run Code Online (Sandbox Code Playgroud)

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

这个问题另一部分.

Ovi*_*tcu 141

如果您还要添加自定义View,xml例如:

 <com.mypack.MyView
      ...
      />
Run Code Online (Sandbox Code Playgroud)

你将需要构造函数public MyView(Context context, AttributeSet attrs),否则你会得到一个ExceptionAndroid尝试膨胀你的View.

如果您添加Viewfrom xml并指定android:style属性,例如:

 <com.mypack.MyView
      style="@styles/MyCustomStyle"
      ...
      />
Run Code Online (Sandbox Code Playgroud)

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

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

  • 关于第三个构造函数,这实际上是**完全错误**.XML**总是**调用双参数构造函数.三参数(和[四参数](https://developer.android.com/reference/android/view/View.html#View(android.content.Context,%20android.util.AttributeSet,%20int,, %20int)))构造函数由**子类**调用,如果他们想要指定包含默认样式的属性,或直接指定默认样式(在四参数构造函数的情况下) (23认同)
  • 何时使用第一个构造函数呢? (3认同)

Jin*_*Jin 112

如果覆盖所有三个构造函数,请不要进行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
}
Run Code Online (Sandbox Code Playgroud)

原因是父类可能在其自己的构造函数中包含您可能意外覆盖的默认属性.例如,这是以下构造函数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);
}
Run Code Online (Sandbox Code Playgroud)

如果你没有打电话super(context),你就不会正确设置R.attr.textViewStyle为样式attr.

  • 这是扩展ListView时的基本建议.作为上述这个级联的(前一个)粉丝,我记得花几个小时跟踪一个微妙的bug,当我为每个构造函数调用正确的super方法时,这个bug就消失了. (11认同)

mbo*_*nin 45

MyView(上下文上下文)

以编程方式实例化视图时使用.

MyView(Context context,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();
}
Run Code Online (Sandbox Code Playgroud)

总而言之

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

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

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


jul*_*les 6

Kotlin似乎减轻了许多痛苦:

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

@JvmOverloads将生成所有必需的构造函数(请参见该注释的文档),每个构造函数都可能调用super()。然后,只需将您的初始化方法替换为Kotlin init {}块。样板代码不见了!