Пользовательские представления 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) вызывается раздувом макета при раздувании макета из 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 

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 конструктора, как вы, скорее всего, захотите.

Цитата из документов :

Для каждого параметра со значением по умолчанию это создаст одну дополнительную перегрузку, которая имеет этот параметр и все параметры справа от него в удаленном списке параметров.

Кажется, это проблема. Я никогда не сталкивался с этим, потому что мои пользовательские представления либо были созданы только в 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 и добавить представление к нему через код.

Вы можете попробовать новую библиотеку Anko для Kotlin из JetBrains (также вы можете участвовать в github ). В настоящее время он находится в бета-версии, но вы можете создавать представления с таким кодом

  button("Click me") { textSize = 18f onClick { toast("Clicked!") } } 

Посмотрите на эту библиотеку