Kotlin: добавляем вьюшки в классы (замена ButterKnife)

Так уж получилось, что ButterKnife в Kotlin не работает. Совсем. При миграции проекта как-то печально видеть NPE после красочно расписанных картин по ее обработке. Совсем немного пришлось копать, чтоб понять — вьюшки не ищутся, листенеры не работают, печаль-тоска. Джейк Вортон аж три года назад запилил версию для Котлина, но с тех пор никаких изменений нет, да и здорово она порезана (см. пункт про листенеры).
“Неужели придется возвращаться к истокам findViewById?”, подумал я.
Отчасти да, кое-где поможет Kotlin. Можно, конечно, использовать и KotterKnife, но его использование ограничено Activity, Fragment, RecyclerView.ViewHolder (там чуть больше, я перечислил лишь те места, где это необходимо мне).
В принципе, для каких-то типичных задач этого хватит, но если вы используете какой-нибудь кастомный класс, в котором обрабатываете опять же кастомные вьюшки, то без головокружительных погружений в пучины нового и старого не обойтись.
Давайте сразу к положительным моментам. Теперь есть быстрый доступ к вьюшкам прямо из активити без создания каких-либо переменных. И это практически вау, если бы не одно “но” — в моих проектах, например, используется 1–2 Activity и десятки фрагментов, где такой возможности нет.
Как это выглядит? Довольно просто. Представьте, что в Activity.setContentView вы добавили layout, в котором есть вьюшка с id = sampleTextView. Доступ к ней осуществляется прям мега просто.
Вуаля, все просто.
А вот как выглядит класс, который наследуется от RecyclerView.ViewHolder
Ничего интересного — просто свойству класса textView автоматом присваивается результат поиска с указанным id.
Давайте лучше покажу как этот момент можно сделать более универсальным, правда в качестве примера будет использоваться кастомный класс, контейнер со вьюшками которого можно доставать и добавлять в какую-нибудь вьюшку, к примеру, активити после выполнения определенных действий.
Боюсь, запутал. Давайте к слайдам.
В конструктор нашего абстрактного класса всегда (!!!) необходимо передавать контекст и id базового лейаута. В свойстве класса view сразу будет создаваться вьюшка на основе этих данных, их использование в дальнейшем не имеет особого смысла, поэтому я их выносить в свойства не стал.
Помимо этого, в классе есть метод create(), который надо тоже ВСЕГДА вызывать после создания объекта. У потомков этого класса будет выполняться метод onCreate(), в котором можно задавать начальную логику, например, выводить текст по умолчанию во вьюшку. Также я решил добавить метод findView, вызывать который будет гораздо проще из любого места, нежели конструкцию, которая в нем описана.
Опять же повторяем трюк, неоднократно описаный выше — свойству textView сразу же присваиваем значение. А в методе onCreate спокойно выводим в нашей вьюшке текст.
Подытожим.
Пришлось уйти от привычных @BindView и вообще от ButterKnife. Довольно печально, ведь к хорошему быстро привыкаешь. С другой стороны, указанная в примере схема позволяет сделать код почище, чем городить огород с вечными itemView.findViewById. Kotlin кое-в-чем все же сделал жизнь проще, и инициализация вьюшек проходит в целом в ту же одну строку.
Не забываем про NULL-check, кстати. То, что было описано в ButterKnife как Optional, здесь может иметь просто обозначение null-safe (и держим в памяти — каждый раз при смене null-safe мы должны это учитывать в коде, да и компилятор может взять красную учительскую ручку)
Если с привычками расставаться сложнее (хотя стойте, зачем тогда с Java решили перейти на Kotlin?), и KotterKnife кажется рабочей схемой, могу подкинуть еще ложечку дегтя — поддержку листенеров (типа OnClick) Джейк не стал добавлять.
No plans. Lambdas are easy enough like you said.
https://github.com/JakeWharton/kotterknife/issues/57
Правда Джейк прав (хоть и отчасти) — лямбды действительно упрощают описание. Давайте посмотрим.
Но что делать, если логика обработки нажатия не умещается в одну — две строки? В этом случае можно поступить двумя способами — оставить как есть (так себе вариант, если честно), либо все же весь код вынести в отдельный метод — пусть он где-то в другом месте валяется.
На этом остановимся, ребята. Всем спасибо
Upd. Для использования в кастомных фрагментах все же лучше использовать KotterKnife — при инициализации объекта использовать метод findView(res: Int) не получится, ведь метод onCreateView выполняется гораздо позже.

