Android 如何使用 Kotlin 创建自定义视图的构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20670828/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create constructor of custom view with Kotlin
提问by Andrii Chernenko
I'm trying to use Kotlin in my Android project. I need to create custom view class. Each custom view has two important constructors:
我正在尝试在我的 Android 项目中使用 Kotlin。我需要创建自定义视图类。每个自定义视图都有两个重要的构造函数:
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
MyView(Context)
is used to instantiate view in code, and MyView(Context, AttributeSet)
is called by layout inflater when inflating layout from XML.
MyView(Context)
用于在代码中实例化视图,并MyView(Context, AttributeSet)
在从 XML 膨胀布局时由布局膨胀器调用。
Answer to this questionsuggests that I use constructor with default values or factory method. But here's what we have:
这个问题的答案表明我使用带有默认值或工厂方法的构造函数。但这是我们所拥有的:
Factory method:
工厂方法:
fun MyView(c: Context) = MyView(c, attrs) //attrs is nowhere to get
class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }
or
或者
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) { ... }
Constructor with default values:
具有默认值的构造函数:
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)
How can this puzzle be resolved?
如何解决这个难题?
UPDATE:Seems like we can use View(Context, null)
superclass constructor instead of View(Context)
, so factory method approach seems to be the solution. But even then I can't get my code to work:
更新:似乎我们可以使用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) { ... }
or
或者
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
回答by aga
Kotlin supports multiple constructors since M11 which was released 19.03.2015. The syntax is as follows:
自 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) {}
}
Edit: you can also use @JvmOverloads annotation so that Kotlin auto-generates the required constructors for you:
编辑:您还可以使用 @JvmOverloads 注释,以便 Kotlin 为您自动生成所需的构造函数:
class MyView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : View(context, attrs, defStyle)
Beware, though, as this approach may sometimes lead to the unexpected results, depending on how the class you inherit from defines its constructors. Good explanation of what might happen is given in that article.
但是请注意,这种方法有时可能会导致意外结果,具体取决于您继承的类如何定义其构造函数。那篇文章对可能发生的事情进行了很好的解释。
回答by colriot
You should use annotation JvmOverloads
(as it looks like in Kotlin 1.0), you can write code like this:
您应该使用注释JvmOverloads
(就像在 Kotlin 1.0 中一样),您可以编写如下代码:
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : View(context, attrs, defStyle)
This will generate 3 constructors just as you most likely wanted.
这将按照您最可能想要的方式生成 3 个构造函数。
Quote from docs:
来自文档的引用:
For every parameter with a default value, this will generate one additional overload, which has this parameter and all parameters to the right of it in the parameter list removed.
对于每个具有默认值的参数,这将生成一个额外的重载,该重载将删除该参数及其右侧的所有参数列表中的参数。
回答by Sagar Jethva
Custome View
with kotlin here's sample code.
CustomeView
与科特林这里的示例代码。
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)
}
}
回答by ajselvig
This does seem to be an issue. I've never run into this because my custom views have either been created only in xml or only in code, but I can see where this would come up.
这似乎是一个问题。我从来没有遇到过这种情况,因为我的自定义视图要么只在 xml 中创建,要么只在代码中创建,但我可以看到这会出现在哪里。
As far as I can see, there are two ways around this:
据我所知,有两种方法可以解决这个问题:
1) Use constructor with attrs. Using the view in xml will work fine. In code, you need to inflate an xml resource with the desired tags for your view, and convert it to an attribute set:
1) 使用带属性的构造函数。使用 xml 中的视图可以正常工作。在代码中,您需要使用您的视图所需的标签来扩充 xml 资源,并将其转换为属性集:
val parser = resources.getXml(R.xml.my_view_attrs)
val attrs = Xml.asAttributeSet(parser)
val view = MyView(context, attrs)
2) Use the constructor without attrs. You can't place the view directly in your xml, but it's easy about to place a FrameLayout in the xml and add the view to it through code.
2) 使用没有 attrs 的构造函数。您不能将视图直接放置在 xml 中,但是很容易将 FrameLayout 放置在 xml 中并通过代码将视图添加到其中。
回答by arekolek
TL;DRmost of the time, it should be enough to just define your custom view as:
TL;DR大多数时候,只需将自定义视图定义为:
class MyView(context: Context, attrs: AttributeSet?) : FooView(context, attrs)
Given this Java code:
鉴于此 Java 代码:
public final class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
its Kotlin equivalent would use secondary constructors:
它的 Kotlin 等效项将使用辅助构造函数:
class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
}
That syntax is useful when you really want to call different super-class constructors depending on whether the view is created in code or inflated from XML. The only case that I know of for this to be true is when you are extending the View
class directly.
当您确实想根据视图是在代码中创建还是从 XML 膨胀来调用不同的超类构造函数时,该语法很有用。我所知道的唯一情况是当您View
直接扩展类时。
You can use a primary constructor with default arguments and a @JvmOverloads
annotation otherwise:
您可以使用带有默认参数和@JvmOverloads
注释的主构造函数,否则:
class MyView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs)
And if you only inflate views from XML, then you can just go with the simplest:
而如果你只从XML膨胀的观点,那么你可以去从最简单的:
class MyView(context: Context, attrs: AttributeSet?) : View(context, attrs)
If your class is open
for extension and you need to retain the style of the parent, you want to go back to the first variant that uses secondary constructors only:
如果您的类open
用于扩展并且您需要保留父类的样式,则您想回到仅使用辅助构造函数的第一个变体:
open class MyView : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
}
But if you want an open
class that overrides the parent style and lets its subclasses override it too, you should be fine with @JvmOverloads
:
但是如果你想要一个open
覆盖父样式并让它的子类也覆盖它的类,你应该没问题@JvmOverloads
:
open class MyView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.customStyle,
defStyleRes: Int = R.style.CustomStyle
) : View(context, attrs, defStyleAttr, defStyleRes)
回答by Sazzad Hissain Khan
There are several ways to override your constructors,
有几种方法可以覆盖构造函数,
When you need default behavior
当您需要默认行为时
class MyWebView(context: Context): WebView(context) {
// code
}
When you need multiple version
当您需要多个版本时
class MyWebView(context: Context, attr: AttributeSet? = null): WebView(context, attr) {
// code
}
When you need to use params inside
当你需要在里面使用 params 时
class MyWebView(private val context: Context): WebView(context) {
// you can access context here
}
When you want cleaner code for better readability
当您想要更简洁的代码以提高可读性时
class MyWebView: WebView {
constructor(context: Context): super(context) {
mContext = context
setup()
}
constructor(context: Context, attr: AttributeSet? = null): super(context, attr) {
mContext = context
setup()
}
}
回答by Martin Zeitler
super()
gives me Primary constructor call expected
(Kotlin 1.3.61
).
super()
给我Primary constructor call expected
(科特林1.3.61
)。
class SomeView(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
): View(context, attrs, defStyleAttr, defStyleRes) {
constructor(context: Context) : this(context, null, 0, 0)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0)
}
回答by b-boy sh1ft
You can try new Library Ankofor Kotlin from JetBrains (also you can contribute on github). Currently it is in beta, but you can create views with such code
您可以从 JetBrains尝试Kotlin 的新库Anko(您也可以在github 上做出贡献)。目前它处于测试阶段,但您可以使用此类代码创建视图
button("Click me") {
textSize = 18f
onClick { toast("Clicked!") }
}
Have a look at this library
看看这个图书馆