Прямые ссылки на публичные уроки для быстрого старта и стабильной индексации lesson-страниц.
После верстки мы изучили немного фундаментала по Android проекту, настало время научиться настраивать взаимодействие кода файла MainActivity.kt с макетом экрана.
У нас есть элементы в макете, которые должны обрабатывать различные действия. Для взаимодействия с ними на View вешается атрибут — id.
android:id="@+id/tvQuestionWord"
Знак + перед словом id сообщает системе, что идентификатор новый и его необходимо создать, а не обратиться к существующему. Плюс проставляется автоматически, но знать об этом рекомендуется.
В сообществе принято называть идентификаторы в стиле camelCase. Дополнительно рекомендуется обозначать тип view в сокращённом виде — от первых слов названия. Например: TextView — tv, Button — btn, EditText — et. Это помогает ориентироваться в элементах при работе с ними в коде: начав вводить btn, дропдаун покажет только кнопки. Стиль именования должен быть единым для всего проекта.
Ещё одна особенность атрибута id: при его наличии система Android может сохранять и восстанавливать состояние элемента при пересоздании Activity или Фрагмента. Например, при изменении ориентации экрана или смене языка Activity пересоздаётся, и введённый пользователем текст может не сохраниться. Наличие атрибута id решает эту проблему.
Код пишется в методе onCreate(), который вызывается при создании экземпляра Activity — в момент создания экрана. Этот метод является частью жизненного цикла Activity, который подробно разберём в отдельном уроке.
Есть два основных способа:
findViewByIdViewBindingПервый способ — классический. Создаём переменную, в которую помещаем ссылку на элемент макета. Способ требует явного указания типа элемента. Поиск ведётся по id через класс R, который хранит все сгенерированные идентификаторы.
val tvQuestionWord: TextView = findViewById(R.id.tvQuestionWord)
Текстовому элементу можно задать новый текст и изменить цвет. Все варианты установки цвета используют функцию setTextColor():
tvHello.setTextColor(Color.GRAY) — передаём цвет из стандартной библиотеки Android напрямую.tvHello.setTextColor(Color.parseColor("#AA0AEF")) — передаём результат parseColor(), который распознаёт Hex-код цвета.tvHello.setTextColor(ContextCompat.getColor(this, R.color.customTextColor)) — передаём цвет из ресурсов. Этот способ используется наиболее часто.ContextCompat — класс библиотеки поддержки Android, обеспечивающий обратную совместимость с системными функциями. В метод getColor() помимо ссылки на цвет передаётся context текущей Activity (this), так как у Activity может быть своя тема, переопределяющая стили текста.
ViewBinding — наиболее актуальный способ в мире XML-вёрстки. Он принципиально отличается от findViewById: генерирует привязку для каждого файла разметки в проекте, что позволяет получать доступ ко всем элементам через один объект. Объект создаётся только один раз при создании Activity — это устраняет необходимость вызывать findViewById для каждого элемента.
Зайдите в файл build.gradle.kts модуля app и добавьте конфигурацию:
buildFeatures { viewBinding = true }
После синхронизации проекта под капотом создаются классы для каждого файла XML-разметки с суффиксом Binding. Например, activity_main.xml → ActivityMainBinding.
Настройка выполняется в два шага:
private lateinit var binding: ActivityMainBinding
lateinit varозначает отложенную инициализацию. Использоватьlateinitбез крайней необходимости не рекомендуется: если обращение к переменной произойдёт до её инициализации, приложение упадёт с труднодиагностируемой ошибкой. Ниже рассмотрим более надёжный подход.
onCreate() сохранить экземпляр привязки и передать корневой элемент разметки в setContentView():class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityLearnWordBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLearnWordBinding.inflate(layoutInflater) setContentView(binding.root) } }
inflate() с параметром layoutInflater создаёт экземпляр binding-класса и связывает его с разметкой Activity — "раздувает" XML в объекты View. Корневой элемент разметки (binding.root) передаётся в setContentView().
Теперь для обращения к элементу по id используется объект binding. При необходимости обратиться к нескольким элементам сразу используется конструкция with {}:
with(binding) { tvHelloWorld.text tvHelloWorld2.text }
Внутри фигурных скобок можно напрямую прописывать id элементов — поля сгенерированного класса ActivityMainBinding.
Backing property — скрытое свойство для хранения другого свойства в классе. Применим этот паттерн к переменной binding, чтобы избавиться от lateinit.
Создаём две переменные:
_binding — изменяемая, с нуллябельным типом, инициализированная null. Используется для хранения значения.binding — неизменяемая, без нижнего подчёркивания. Именно к ней обращаемся в коде для работы с элементами разметки.Переменной binding кастомизируем геттер: если _binding не null — возвращаем значение, иначе выбрасываем IllegalStateException с информационным сообщением:
private var _binding: ActivityMainBinding? = null private val binding get() = _binding ?: throw IllegalStateException("Binding for ActivityMainBinding must not be null")
Теперь если обращение к binding произойдёт до инициализации в onCreate(), приложение упадёт с понятной ошибкой — вы будете знать место и причину. Это особенно важно в крупных проектах с большим количеством Activity и Фрагментов.
Backing property обеспечивает безопасность при обращении к свойству binding: оно всегда будет иметь ненулевое значение.