Пользовательские виды Android с Kotlin
Я пытаюсь использовать Kotlin в своем Android-проекте. Мне нужно создать пользовательский класс представления. Каждое пользовательское представление имеет два важных конструктора:
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) вызывается layout inflater при раздувании layout из XML.
Ответ на этот вопрос предполагает, что я использую конструктор со значениями по умолчанию или фабричный метод. Но вот что мы имеем:
Фабричный метод:
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)
Как можно разрешить эту загадку?
UPDATE: Похоже, что мы можем использовать конструктор суперкласса 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
5 ответов:
Kotlin поддерживает несколько конструкторов начиная с M11, который был выпущен 19.03.2015. Синтаксис выглядит следующим образом:
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 1.0), вы можете написать код следующим образом:class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : View(context, attrs, defStyle)Это создаст 3 конструктора, как вы, скорее всего, хотели.
Цитата из docs :
Для каждого параметра со значением по умолчанию будет сгенерировано одно значение. дополнительная перегрузка, которая имеет этот параметр и все параметры, чтобы справа от него в списке параметров удалено.
Похоже, это действительно проблема. Я никогда не сталкивался с этим, потому что мои пользовательские представления были созданы либо только в 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 и добавить представление к нему через код.
Custome
Viewс kotlin вот пример кода.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) } }
Вы можете попробовать новую библиотеку Anko для Kotlin из JetBrains (также вы можете внести свой вклад в github). В настоящее время он находится в бета-версии, но вы можете создавать представления с таким кодом
button("Click me") { textSize = 18f onClick { toast("Clicked!") } }Взгляните на эту библиотеку
Comments