关于Kotlin jvmoverloads的误解,用于创建Android视图

TL; DR (TL;DR)

No fear of using @JvmOverloads to create Android custom views. All you need to do is to provide a default constructor with only first 2 parameters(Context & AttributeSet). Do NOT provide defStyleAttr and its default value at all.

不用担心使用@JvmOverloads创建Android自定义视图。 您需要做的就是为默认构造函数仅提供前两个参数( ContextAttributeSet )。 完全不提供defStyleAttr及其默认值。

问题与误解 (Problem & Misconception)

I joined a company that has heavily adopted airbnb’s Epoxy framework in the view layer of their Android apps. As result, we have a very large collection of custom views by extending existing view subclasses, such as TextInput and Button). The traditional technique was to override(note that it is override, not overload) superclass’ multiple constructors:

我加入了一家在其Android应用的视图层中大量采用airbnb的Epoxy框架的公司。 因此,通过扩展现有的视图子类(例如TextInputButton ),我们拥有大量的自定义视图 。 传统技术是重写 (注意,它是override ,而不是overload )超类的多个构造函数:

class CustomTextInput : TextInputEditText {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}

Like Java, it is verbose, right? So many Kotlin developers like to use a language feature called @JvmOverloads to simplify its creation.

像Java一样,它很冗长吧? 因此,许多Kotlin开发人员喜欢使用一种名为@JvmOverloads的语言功能来简化其创建。

class CustomTextInput @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : TextInputEditText(context, attrs, defStyleAttr)

Note that in the snippet above, we give default value 0 to argument defStyleAttr(default style attribute), meaning there is no default style. Sometime, that is fine, but other times, unexpectedly, the custom view does not appear the way as its superclass.

请注意,在上面的代码段中,我们给参数defStyleAttr(default style attribute)赋予了默认值0 ,这意味着没有默认样式。 有时,这很好,但有时,出乎意料的是,自定义视图并未以其超类的方式出现。

Image for post
TextInput - Unexpected effect when @JvmOverloads with defStyleAttr defaults to 0
TextInput-带有defStyleAttr的@JvmOverloads默认为0时的意外效果

Because the problems manifest for some view widgets but not others, so it confused a lot of developers. Also, due to the influence of some popular/great blogposts(but different opinion and conclusion), people are avoiding using @JvmOverloads after all. For example:

由于问题仅在某些视图小部件中显现,而在其他视图小部件中则没有,因此它使很多开发人员感到困惑。 另外,由于一些受欢迎/出色的博客文章的影响(但观点和结论不同),人们毕竟避免使用@JvmOverloads 。 例如:

I would like to offer my understanding of the issue and voice my opinion and solution.

我想表达我对问题的理解,并表达我的意见和解决方案。

为什么 (Why)

The root cause is that @JvmOverloads is a trick from Kotlin compiler that generates overload constructors, instead of the override constructors.

根本原因是@JvmOverloads是Kotlin编译器的一个技巧,它会生成重载构造函数,而不是重写构造函数。

Image for post
Multiple constructor overrides
多个构造函数覆盖

But when we use @JvmOverloads with 3 constructor arguments and 2 default values like this(same as in earlier problem section):

但是,当我们将@JvmOverloads与3个构造函数参数和2个默认值一起使用时(与前面的问题部分相同):

class CustomTextInput @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : TextInputEditText(context, attrs, defStyleAttr)

The compiler-generated java code would look like:

编译器生成的Java代码如下所示:

@JvmOverloads
public CustomTextInput(@NotNull Context context, @Nullable AttributeSet attrs, int defStyleAttr){
    super(context, attrs, defStyleAttr);
}
@JvmOverloads
public CustomTextInput(@NotNull Context context, @Nullable AttributeSet attrs){
    super(context, attrs, 0);
}
@JvmOverloads
public CustomTextInput(@NotNull Context context){
    super(context, null, 0);
}

When superclass’ defStyleAttr happens to be 0, the generated code just happen to work, as illustrated here:

当超类的defStyleAttr恰好为0时 ,生成的代码就可以正常工作,如下所示:

Image for post
Works fine when defStyleAttr=0 in superclass
当超类中的defStyleAttr = 0时工作正常

But if superclass’ defStyleAttr is not 0, we are running into trouble. E.g. TextInputEditText uses attr.editTextStyle as the default style.

但是,如果超类的defStyleAttr不为0,则会遇到麻烦。 例如, TextInputEditText使用attr.editTextStyle作为默认样式。

public TextInputEditText(Context context, AttributeSet attrs) {
    this(context, attrs, attr.editTextStyle);
}

Our generated overloaded constructors will fail to render the style as superclass, because we pass an unmatched defStyleAttr:

我们生成的重载构造函数将无法将样式呈现为超类,因为我们传递了不匹配的defStyleAttr

Image for post
Generated overloaded constructors failed to render because of mismatched defStyleAttr
生成的重载构造函数由于defStyleAttr不匹配而无法呈现

破解解决方案 (Hack Solution)

Provide the matching defStyleAttr as default argument value:

提供匹配的defStyleAttr作为默认参数值:

class TextInputEditTextJvmStyled @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = editTextStyle
) : TextInputEditText(context, attrs, defStyleAttr)

But this is a hack solution because you always have to peek into the internal implementation of superclasses and that breaks the encapsulation principle and is error-prone.

但这是一个hack解决方案,因为您总是必须窥视超类的内部实现,并且这破坏了封装原理并且容易出错。

最佳解决方案(TL; DR) (Best Solution (TL;DR))

If you want to use Kotlin’s @JvmOverloads for Android custom view creation, just apply to only first 2 parameters. Do NOT provide defStyleAttr as argument and its default value at all.

如果要使用Kotlin的@JvmOverloads进行Android自定义视图的创建,只需将其仅应用于前两个参数。 完全不提供defStyleAttr作为参数及其默认值。

class TextInputEditTextJvmOverloads2 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : TextInputEditText(context, attrs)

Why does this “lazy” solution always work? Because we are using override on the 2-parameter constructor of the superclass, so whatever the default style that the superclass uses will still carry over. Here is the illustration:

为什么这种“惰性”解决方案始终有效? 因为我们在超类的2参数构造函数上使用了重写,所以无论超类使用哪种默认样式,都将继续使用。 这是插图:

Image for post

A sample app as illustration on Github:

在Github上以示例应用程序为例:

Great article to understand different constructors of an Android view:

伟大的文章,了解Android视图的不同构造函数:

翻译自: https://proandroiddev.com/misconception-about-kotlin-jvmoverloads-for-android-view-creation-cb88f432e1fe

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值