我正在尝试在我的Android项目中使用Kotlin.我需要创建自定义视图类.每个自定义视图都有两个重要的构造函数
public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } }
MyView(Context)
用于在代码中实例化视图,并MyView(Context, AttributeSet)
在从XML 扩展布局时由布局inflater调用.
对这个问题的回答表明我使用默认值或工厂方法的构造函数.但这就是我们所拥有的:
工厂方法:
fun MyView(c: Context) = MyView(c, attrs) //attrs is nowhere to get class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }
要么
fun MyView(c: Context, attrs: AttributeSet) = MyView(c) //no way to pass attrs. //layout inflater can't use //factory methods class MyView(c: Context) : View(c) { ... }
具有默认值的构造方法:
class MyView(c: Context, attrs: AttributeSet? = null) : View(c, attrs) { ... } //here compiler complains that //"None of the following functions can be called with the arguments supplied." //because I specify AttributeSet as nullable, which it can't be. //Anyway, View(Context,null) is not equivalent to View(Context,AttributeSet)
如何解决这个难题?
更新:似乎我们可以使用View(Context, null)
超类构造函数代替View(Context)
,因此工厂方法方法似乎是解决方案.但即使这样我也无法使用我的代码:
fun MyView(c: Context) = MyView(c, null) //compilation error here, attrs can't be null class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }
要么
fun MyView(c: Context) = MyView(c, null) class MyView(c: Context, attrs: AttributeSet?) : View(c, attrs) { ... } //compilation error: "None of the following functions can be called with //the arguments supplied." attrs in superclass constructor is non-null
colriot.. 52
你应该使用注释JvmOverloads
(就像在Kotlin 1.0中看起来一样),你可以编写如下代码:
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : View(context, attrs, defStyle)
这将生成3个构造函数,就像您最想要的那样.
从文档引用:
对于具有默认值的每个参数,这将生成一个额外的重载,该重载具有此参数,并且删除了参数列表中右侧的所有参数.
这种方式比接受的答案更优雅. (4认同)
"...考虑到这个解决方案对于所有类型的视图都不可行.其中一些(例如`TextView`)需要调用`super` parent来初始化样式." - [使用Kotlin安装自定义视图(KAD 06)](https://antonioleiva.com/custom-views-android-kotlin/) (4认同)
aga.. 43
自2015年3月19日发布的M11以来,Kotlin支持多个构造函数.语法如下:
class MyView : View { constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { // ... } constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {} }
更多信息在这里和这里.
编辑:您还可以使用@JvmOverloads注释,以便Kotlin为您自动生成所需的构造函数:
class MyView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : View(context, attrs, defStyle)
但要注意,因为这种方法有时会导致意外结果,具体取决于您继承的类如何定义其构造函数.在该文章中给出了可能发生的事情的良好解释.
Custome View
这里科特林的示例代码.
class TextViewLight : TextView { constructor(context: Context) : super(context){ val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5); setTypeface(typeface) } constructor(context: Context, attrs : AttributeSet) : super(context,attrs){ val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5); setTypeface(typeface) } constructor(context: Context, attrs: AttributeSet , defStyleAttr : Int) : super(context, attrs, defStyleAttr){ val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5); setTypeface(typeface) } }
自2015年3月19日发布的M11以来,Kotlin支持多个构造函数.语法如下:
class MyView : View { constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { // ... } constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {} }
更多信息在这里和这里.
编辑:您还可以使用@JvmOverloads注释,以便Kotlin为您自动生成所需的构造函数:
class MyView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : View(context, attrs, defStyle)
但要注意,因为这种方法有时会导致意外结果,具体取决于您继承的类如何定义其构造函数.在该文章中给出了可能发生的事情的良好解释.
你应该使用注释JvmOverloads
(就像在Kotlin 1.0中看起来一样),你可以编写如下代码:
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : View(context, attrs, defStyle)
这将生成3个构造函数,就像您最想要的那样.
从文档引用:
对于具有默认值的每个参数,这将生成一个额外的重载,该重载具有此参数,并且删除了参数列表中右侧的所有参数.
这确实是一个问题.我从来没有遇到过这种情况,因为我的自定义视图要么只在xml中创建,要么只在代码中创建,但我可以看到它会出现在哪里.
据我所知,有两种解决方法:
1)使用带有attrs的构造函数.使用xml中的视图可以正常工作.在代码中,您需要使用所需的视图标记为xml资源充气,并将其转换为属性集:
val parser = resources.getXml(R.xml.my_view_attrs) val attrs = Xml.asAttributeSet(parser) val view = MyView(context, attrs)
2)使用没有attrs的构造函数.您不能将视图直接放在xml中,但很容易将FrameLayout放在xml中并通过代码将视图添加到其中.