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

sashatinkoff
Aug 25, 2017 · 3 min read

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

О разработке для Android

Нет единственно правильного решения любой задачи, их (решений) — множество. Я предлагаю вам одни варианты и не настаиваю на их использовании.

)

sashatinkoff

Written by

Пишу о разном с матом ем булку с маком никогда не бегал с автоматом

О разработке для Android

Нет единственно правильного решения любой задачи, их (решений) — множество. Я предлагаю вам одни варианты и не настаиваю на их использовании.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade