Anko docs сообщает нам, как добавлять пользовательские представления в DSL. Но если мое пользовательское представление представляет собой группу представлений, возникают проблемы.
class MyFrameLayout(context: Context) : FrameLayout(context) fun ViewManager.myFrameLayout(init: MyFrameLayout.() -> Unit = {}) = ankoView({ MyFrameLayout(it) }, init) class MyUI : AnkoComponent<Fragment> { override fun createView(ui: AnkoContext<Fragment>) = with(ui) { myFrameLayout { textView("hello").lparams { // error: Unresolved reference: lparams bottomMargin = dip(40) } } } }
но если я myFrameLayout
вызов frameLayout
на frameLayout
он работает нормально. Итак, каков способ использования групп представлений с Anko DSL?
На самом деле вам просто нужно расширить anko и объявить свой заказ, а затем использовать его в DSL в обычном режиме:
public inline fun ViewManager.customView() = customView {} public inline fun ViewManager.customView(init: CustomView.() -> Unit) = ankoView({ CustomView(it) }, init)
Затем используйте его в DSL нормально
frameLayout { customView() }
если вы наследуете, например, _RelativeLayout
вместо RelativeLayout
, вы можете использовать свой собственный макет, как и следовало ожидать.
Если вы перейдете к любой из деклараций lparams
от вашего кода, вы увидите, что внутри созданного Anko DSL-кода lparams
является функцией расширения для T: View
которая выглядит так:
fun <T: View> T.lparams( width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT, height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT, init: android.widget.FrameLayout.LayoutParams.() -> Unit = defaultInit ): T { val layoutParams = android.widget.FrameLayout.LayoutParams(width, height) layoutParams.init() this@lparams.layoutParams = layoutParams return this }
(и больше перегрузок для разных конструкторов LayoutParams
)
Он объявляется внутри класса, поэтому он видим только в функциях с приемником этого класса, см. Другой вопрос об этом методе программирования DSL.
Чтобы иметь возможность использовать lparams
для вашей пользовательской ViewGroup
в Anko DSL, вы должны объявить аналогичную функцию или функции внутри своего пользовательского кода просмотра, что создаст соответствующие LayoutParams
для вашего класса.
Если вы хотите также скрыть функцию lparams
из-за пределов DSL, вы можете сделать подкласс MyFrameLayout
и использовать его только в DSL-коде, работая с MyFrameLayout
самостоятельно в другом месте.
После этого вы можете вызвать lparams
на любом View
внутри лямбда, которое вы передаете как init: MyFrameLayout.() -> Unit
to fun ViewManager.myFrameLayout
.
Если мы посмотрим на источники Anko, мы увидим, что frameLayout
возвращает экземпляр класса _FrameLayout
, где определены эти функции lparams
. По моему lparams
это необходимо, поэтому эти функции lparams
доступны только в коде построения макета.
В файле Layouts.kt
Anko есть следующие классы _<ViewGroup>
для каждой поддерживаемой группы ViewGroup
.
Таким образом, простой способ поддержки настраиваемой группы просмотра состоит в создании класса _<ViewGroup>
с lparams
методов lparams
. Проблема в том, что этот класс _<ViewGroup>
часто содержит гораздо больше кода, чем моя <ViewGroup>
!
И если я хочу создать много настраиваемых групп просмотра, добавление поддержки Anko для них становится большой болью при таком подходе. Предположим, у меня есть MyFrameLayout1
, MyFrameLayout2
, MyFrameLayout3
. Они в основном являются FrameLayout
, поэтому я хочу, чтобы с ними использовались те же параметры макета. Но мне нужно создать _FrameLayout1
, _FrameLayout2
, _FrameLaoyt3
которые просто копируют / _FrameLayout
Anko.
Поэтому я немного улучшил этот подход. Я создаю interface _FrameLayout
:
interface _FrameLayout { // copy/paste from Anko's _FrameLayout }
и теперь для поддержки любого пользовательского подкласса FrameLayout
мне просто нужно:
class _MyFrameLayout(ctx: Context) : MyFrameLayout(ctx), _FrameLayout fun ViewManager.myFrameLayout(init: _MyFrameLayout.() -> Unit = {})= ankoView({ _MyFrameLayout(it) }, init)
Это уменьшает количество копий и папок при создании множества настраиваемых групп представлений.