Уроки Kotlin в Android Studio: onClickListener - нажатие кнопки

Нажатие кнопки — это то, с чего начинается любое взаимодействие пользователя с приложением. Каждый раз, когда вы тапаете по экрану, срабатывает специальный «слушатель», который ждёт этого момента. Kotlin позволяет написать его очень коротко, но за лаконичностью скрывается несколько разных подходов. Этот материал — продолжение наших занятий в Android Studio, где мы разложим по полочкам, как правильно подходить к обработке кликов и почему в разных ситуациях удобнее применять разные приёмы.


Самый короткий путь — лямбда

В одном из прошлых уроков мы уже использовали такой код, когда создавали первую кнопку. Это действительно самый простой способ: внутри фигурных скобок пишется то, что должно произойти после нажатия. Напишем ещё раз, чтобы освежить в памяти:

myButton.setOnClickListener {
    myText.text = "Кнопка нажата!"
}

За кулисами здесь создаётся объект, который реализует интерфейс с одним методом. Но нам не нужно задумываться об этом — всю работу берёт на себя язык. Такой стиль подходит для большинства повседневных задач, особенно когда обработчик состоит из одной-двух строк.


Анонимный класс — чуть больше контроля

Иногда лямбды недостаточно, особенно если вы только начинаете и хотите видеть полную картину. Внутри фигурных скобок можно явно прописать анонимный класс, который наследует View.OnClickListener и переопределяет его метод onClick. Выглядит это чуть длиннее, зато хорошо заметно, что именно происходит:

myButton.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        myText.text = "Нажали на кнопку"
    }
})

Такой способ применяют, когда нужно передать обработчик в другое место программы или когда внутри требуется доступ к чему-то ещё, что неудобно захватывать в лямбду. Однако для коротких действий он выглядит громоздко, и в современном Kotlin его используют реже.


Один метод для нескольких кнопок

Когда на экране много кнопок, неудобно писать отдельный обработчик для каждой. Гораздо проще назначить один и тот же метод, а внутри различать, кто именно нажат. Для этого Activity или Fragment могут реализовать интерфейс View.OnClickListener и переопределить его метод onClick, а кнопкам указать setOnClickListener(this).

class MainActivity : AppCompatActivity(), View.OnClickListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button1.setOnClickListener(this)
        button2.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.button1 -> showMessage("Первая кнопка")
            R.id.button2 -> showMessage("Вторая кнопка")
        }
    }

    private fun showMessage(text: String) {
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
    }
}

Код получается чистым: все обработчики собраны в одном месте, не нужно искать разбросанные лямбды по всему файлу. Это особенно удобно, если количество кнопок растёт или логика обработки одинаковая.


Вынос обработчика в отдельную функцию

Можно не реализовывать интерфейс, а просто создать обычную функцию и передать ссылку на неё. Для этого функция должна принимать один параметр типа View и ничего не возвращать:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button1.setOnClickListener(::onButtonClick)
        button2.setOnClickListener(::onButtonClick)
    }

    private fun onButtonClick(view: View) {
        when (view.id) {
            R.id.button1 -> showMessage("Нажата кнопка 1")
            R.id.button2 -> showMessage("Нажата кнопка 2")
        }
    }
}

Ссылка на функцию ::onButtonClick передаётся вместо лямбды, и Kotlin понимает, что от неё требуется. Этот подход даёт ещё больше порядка: обработчик лежит отдельно, его можно тестировать независимо и использовать повторно.


Пример с ViewBinding и обработкой в Compose

Если вы переходите на Jetpack Compose, забудьте про OnClickListener в привычном виде — здесь всё строится на composable-функциях. У каждой кнопки есть параметр onClick, куда передаётся обычная лямбда:

Button(onClick = {
    // действие
}) {
    Text("Нажми меня")
}

Compose сам следит за состоянием кнопки и анимацией нажатия, а вам остаётся только описать, что должно произойти.


Что мы сегодня узнали

Сегодняшний урок показал, что в среде Android Studio существует несколько способов отреагировать на касание экрана, и язык Kotlin даёт свободу выбрать самый читаемый под конкретную задачу. Вы освоили лямбды, анонимные классы, единый обработчик для нескольких элементов и даже заглянули в мир Compose. Всё это — необходимые инструменты, которые делают код понятным и удобным для доработки. Теперь попробуйте добавить в свой проект три кнопки и обработать их разными приёмами — тогда каждый из рассмотренных подходов закрепится на практике.