Android — как добиться setonclicklistener в kotlin?

Введение

Для работы с навигацией между экранами в Android-приложении применяется библиотека Navigation Component. Она входит в набор Android Jetpack и помогает реализовать навигацию, от простых нажатий на кнопки до более сложных, таких как панели приложений (action bars) и панель навигации (navigation drawers).

Библиотека предоставляет ряд преимуществ:

  • Корректная обработка кнопок «Вверх» и «Назад» по умолчанию
  • Поведение по умолчанию для анимации и переходов.
  • Реализация шаблонов навигации пользовательского интерфейса (таких, как navigation drawer).
  • Безопасность типов при передаче информации во время навигации.
  • Инструменты Android Studio для визуализации и редактирования navigation flow приложения.

В качестве примера будет использоваться готовый проект, в котором уже есть все необходимые экраны, их макеты и исходные коды. Необходимо будет лишь разобраться с навигацией между экранами.

Тестовое приложение представляет собой мини-викторину. Оно будет иметь несколько экранов:

  • Стартовый экран с логотипом и кнопкой «Play».
  • Экран с вопросами и вариантами ответа.
  • Экран с кнопкой «Try Again» в случае ошибки.
  • Экран с кнопкой «Next Match» в случае успешного прохождения теста.
  • Экран «About» с информацией о приложении.
  • Экран «Rules» с текстом правил приложения.

Стартовый проект с приложением содержит все эти экраны, но не содержит связей между ними, их создание будет рассмотрено далее. Каждое описанный экран — это фрагмент. Фрагмент — особый компонент системы Android. Прежде чем начать разбираться с навигацией по фрагментам, нужно разобраться с тем, что это такое.

Анонимный класс

Самый простой способ создания обработчика это использование анонимного класса.

mButton.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick(View v) {
        // ... 
    }
});

Преимуществом такого подхода является простота и не требует создания дополнительных переменных. Очень хорошо подходит, если у вас только одна кнопка. Недостатками является внутренняя реализация, обработчик нельзя задать в другом месте и при наличии нескольких кнопок, такой подход становиться массивным. Так же недостатком является то, что обработчик будет использоваться только данной кнопкой.

Отображение сообщения по нажатию кнопки

На этом шаге мы добавим функцию отображения тоста – короткого сообщения на экране. Функция будет выполняться при нажатии кнопки с надписью TOAST.

Откройте класс MainActivity.kt. (раскройте ветвь app > java > com.example.android.myfirstapp чтобы найти MainActivity). Этот класс описывает поведение главного экрана нашего приложения.Пока что класс содержит только одну функцию, onCreate(). Функция onCreate() выполняется, когда активити стартует.
Посмотрите внимательно на функцию onCreate()

Обратите внимание на вызов функции setContentView(). Эта строка устанавливает файл ресурсов activity_main.xml в качестве разметки активити главного экрана.

setContentView(R.layout.activity_main);

1 setContentView(R.layout.activity_main);
  1. Добавим новую функцию toastMe() в класс MainActivity. Функция toastMe() принимает один аргумент — View. Это представление, получающее событие нажатия кнопки.Функция создает и отображает всплывающее уведомление. Вот ее код:

fun toastMe(view: View) {
  // val myToast = Toast.makeText(this, message, duration);
  val myToast = Toast.makeText(this, «Hello Toast!», Toast.LENGTH_SHORT)
  myToast.show()
}

1
2
3
4
5

fun toastMe(viewView){

 // val myToast = Toast.makeText(this, message, duration);

 val myToast=Toast.makeText(this,»Hello Toast!»,Toast.LENGTH_SHORT)

 myToast.show()

}

В языке Kotlin, если явно не используется никакого модификатора доступа, то по умолчанию применяется public .  Далее идет слово fun, обозначающее функцию, и ее имя. В скобках передаваемый функции аргумент – его имя и тип разделены двоеточием. Далее объявляется переменная val myToast. Словом val обозначаются переменные «только для чтения», значение которых обычно задается только один раз. Обычные изменяемые переменные обозначаются в языке Kotlin словом var. Далее переменной myToast присваивается результат вызова метода makeText java-класса Toast. Метод makeText принимает контекст, сообщение и длительность отображения тоста, и возвращает тост в переменную  myToast. Тост затем отображается методом show().

Функция toastMe является примером использования java-кода в kotlin-классе.

  1. Откройте файл макета activity_main.xml и добавьте свойство android:onClick кнопкеToast. Значением свойства установите toastMe.

android:onClick=»toastMe»

1 androidonClick=»toastMe»
  1. Запустите приложение и нажмите кнопку TOAST, вы должны увидеть на экране короткое сообщение с текстом «Hellо Toast!».

Таким образом, что для того, чтобы сделать элемент экрана интерактивным вам нужно:

  • Реализовать функцию, определяющую поведение экранного элемента при нажатии на него. Эта функция должна быть public, не возвращать никаких значений, и принимать View в качестве аргумента.
  • Установить имя функции в качестве значения свойства onClick в экранном элементе.

Заставляем кнопки хоть что-то делать!

Теперь у нас есть макет, и хорошая новость заключается в том, что  ссылаться на эти элементы и изменять их в программе Kotlin очень легко.

Для этого нам нужно обратиться к «ID» (идентификатору), которые мы предоставили нашим вьюшкам. View — это техническое название виджетов, составляющих наш макет, таких как кнопки и текстовые метки.

Вы увидите, что мы уже сделали это в XML:

В данном случае «ID» кнопки — «button»

Обратите внимание, что  написание чувствительно к регистру. Слово Button с заглавной буквы B на самом деле относится к более широкой концепции всех кнопок в Kotlin

Следовательно, мы можем ссылаться на кнопку в нашем коде. Если бы мы написали button.setText («Right Answer!»), тогда текст на первой кнопке изменился бы на Right Answer!.

Но мы не хотим этого делать. Вместо этого мы сделаем так, чтобы при нажатии первой кнопки появлялось CORRECT (ПРАВИЛЬНО!) и этим мы сообщаем нашему игроку, что он выбрал правильное решение.

Для этого мы собираемся вывести на экран сообщение, известное как «toast message» (всплывающее сообщение-подсказка). Это небольшой плавающий пузырь с текстом, который исчезает через пару секунд.

Не беспокойтесь о строке с текстом, создающей и отображающей это всплывающее сообщение, вы будете ей редко пользоваться, и это часть Android, а не Kotlin.

Мы собираемся сделать так, чтобы эти сообщения отображались, когда мы нажимаем на одну из этих кнопок:

Поместите этот код в функцию onCreate (это весь код в фигурных скобках после слова onCreate). В Kotlin, как и в Java, код можно «заблокировать», поместив его в фигурные скобки. Такое группирование полезно, если мы хотим, чтобы определенный набор кода выполнялся, например, за пределами прямолинейной траектории. И совсем скоро мы узнаем, что же такое «function» (функция).

Нажмите зеленую кнопку воспроизведения в Android Studio IDE, убедившись, что виртуальное устройство настроено или устройство Android подключено. Вы увидите, что игра появится на экране, и вы сможете выбрать вариант ответа. Вот так да! Клик на «A» отобразит правильное сообщение, а два других должны отобразить неправильное сообщение.

AutoCompleteTextView

Компонент AutoCompleteTextView — это текстовое поле с автозаполнением и возможностью редактирования вводимого текста. Использование компонента удобно в том случае, когда требуется ускорить процесс ввода текста.

На панели инструментов элемент можно найти в разделе Texts.

AutoCompleteTextView является подклассом EditText, поэтому доступны все возможности форматирования и редактирования текста родительского класса.

Дополнительно, у AutoCompleteTextView есть свойство completionThreshold для указания минимального числа символов, которое должен ввести пользователь, чтобы включилась функция автозаполнения. Для связывания с данными необходимо задействовать адаптер, содержащий список значений через метод setAdapter().

Для упрощения создадим статический массив строк с именами котов и свяжем его через адаптер с нашим элементом в Java-коде. В реальных приложениях лучше использовать строковые ресурсы.

Запустите пример и начинайте вводить имена. Если вы напечатаете два символа му, то появится выпадающий список с вариантами слов для продолжения. Дальше текст можно не набирать, а просто коснуться нужного варианта и готовый текст сразу вставится в текстовое поле. Вот так все просто и работает.

Пример с строковыми ресурсами (res/values/string.xml).

Получаем массив из ресурсов и используем в адаптере.

Альтернативные варианты

Существуют альтернативные варианты для обработки щелчка кнопки, которые могут встретиться вам в интернете. Со временем вы сами выберите удобный вам стиль.

Например, кнопку и метку можно объявить стандартным Java-способом через метод findViewById() и затем подключать слушатель к кнопке.

На самом деле Java-стиль в Kotlin практически никто не использует. А вот ещё пара примеров для Kotlin.

Новички на первых порах часто делают элементарную ошибку — помещают код работы с компонентами до метода setContentView(). Запомните, сначала нужно вывести шаблон (R.layout.activity_main), а только потом можно работать с кнопками, текстовыми полями, переключателями и т.д. В нашем случае используется отдельный метод для кнопки, который формируется после метода setContentView().

Добавьте в проект новую кнопку и разместите под первой кнопкой. Подправьте несколько атрибутов, например, так.

Я поменял для кнопки текст и идентификатор, а также некоторые параметры, отвечающие за положение кнопки на экране. Переключитесь в графический режим и посмотрите, как выглядит кнопка.

Снова открываем файл MainActivity.kt и пишем следующий код сразу после объявления класса и до метода onCreate():

В методе onCreate() после кода для первой кнопки пишем:

Ещё раз напоминаю, не копируйте текст со страницы, а вводите текст вручную. Так вы быстрее освоите программирование, кроме того научитесь пользоваться системой подсказок и уменьшите количество опечаток.

В этом примере мы создали дополнительную переменную-счётчик counter, которая будет содержать число подсчитанных ворон. После создания мы сразу её инициализировали.

В обработчике щелчка мы увеличиваем значение счётчика через оператор инкремента ++, который ставим перед переменной. Попробуйте разместить оператор после переменной и посмотреть на результат, так вы поймёте разницу от местоположения оператора у переменной.

Запускаем приложение и начинаем щёлкать по кнопке. При каждом щелчке счётчик counter будет увеличиваться на единицу и эта информация будет отображаться на экране.

Теперь у вас есть чрезвычайно полезное приложение Счётчик ворон. Если преподаватель вас неожиданно спросит на занятии, почему вы смотрите в окно с рассеянным видом, вы можете смело достать свой телефон и сказать, что заняты очень важным делом — считаете ворон.

Впрочем, программа получилась у нас универсальной, можно считать не только ворон за окном.

Используем фильтры

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

На практике встречается реже, но может пригодиться. Допустим, у вас уже есть вторая активность. В манифесте добавим к ней специальный фильтр:

И запускаем вторую активность через щелчок кнопки таким способом.

Заменим длинную строку на константу.

Итак, что мы сделали. Для второй активности мы прописали фильтр и указали имя для action в атрибуте android:name. Для удобства я просто поместил в него полное имя активности с названием пакета. Конструктор класса Intent имеет несколько перегруженных версий. В одной из версий можно указать строку для действия. Мы указали своё созданное действие, которое прописано у второй активности. Система во время работы просматривает манифесты всех установленных приложений. При поиске соответствия система находит наш фильтр и запускает нужную активность.

По такому же принципу можно запустить другие активности. Посмотрите на пример . Если вы скопируете пример к себе и посмотрите на документацию по android.provider.Settings.ACTION_AIRPLANE_MODE_SETTINGS, то увидите, что этому коду соответствует строковая константа public static final java.lang.String ACTION_AIRPLANE_MODE_SETTINGS = «android.settings.AIRPLANE_MODE_SETTINGS». Сравните с нашим кодом. Вы можете предположить, что у активности настроек для автономного режима в фильтре прописана эта строка.

Имя категории фильтра android.intent.category.DEFAULT говорит системе, что следует выполнить действие по умолчанию, а именно, запустить активность. Существует и другие имена, которые пока нас не интересуют.

А теперь вопрос на засыпку. Что произойдёт, если создать ещё одну активность и указать такой же фильтр, как у второй активности? А давайте проверим. Создайте у себя третью активность и скопируйте блок с фильтром от второй активности в него.

Щёлкаем по кнопке в первой активности. Система попросит выбрать нужный вариант.

Если вы выберите пункт ALWAYS, то в следующий раз выбирать не придётся. Чтобы сбросить выбор, зайдите в свойства приложения в Настройках и найдите кнопку Clear defaults.

Делаем игру увлекательной

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

Затем мы изменим наши новые приемы программирования, чтобы превратить это в настоящую игру.

Сначала мы создадим еще три переменные. Это будут глобальные переменные, созданные вне каких-либо функций и, следовательно, доступные для всех функций.

Обратите внимание, что в Kotlin вам не нужно назначать тип переменной. В таких языках, как Java, вам нужно сразу указать, является ли ваша переменная Int (целым числом), String (строкой), Float (числом с плавающей точкой) и т

д. В Kotlin мы можем просто написать var и позволить Kotlin разбираться самостоятельно!

Перед функцией onCreate() добавьте эти три строки:

Переменная questionNo — это целое число и будет использоваться для отслеживания номера вопроса, который мы задаем. Переменная questions представляет из себя список, означающий, что это переменная, которая может содержать несколько значений — в данном случае несколько строк

Каждая из наших строк разделена запятой, и ей будет присвоен индекс, на который можно будет ссылаться позже (обратите внимание, что первому элементу в списке присваивается индекс: 0)

Важно, что символ \n распознается Kotlin (и большинством языков) как символ «новой строки» и фактически не отображается в нашем выводе

(Это выглядит некрасиво, и если вы создаете реальное приложение, то лучше хранить эти значения в отдельном XML-файле.)

Последняя строка создает еще один список, на этот раз заполненный целыми числами. Это правильные ответы на каждый наш вопрос!

Затем мы создаем новую функцию с названием updateQuestion. Все, что мы собираемся здесь сделать, это изменить наш вопрос в зависимости от номера вопроса. Делаем это так:

Поскольку мы вызываем эту функцию только тогда, когда кто-то получает правильный ответ, мы можем безопасно увеличить значение нашей переменной questionNo в начале функции. Затем мы собираемся задать вопрос, который отображается, обновив textView.

Когда мы используем get со списком как в коде выше, нам нужно только вставить индекс в скобки, чтобы получить значение, которое там помещено. Таким образом, мы можем перейти к следующему вопросу, постепенно увеличивая это значение.

Наконец, мы меняем наше условие «right answer» (правильный ответ) на любую правильную запись в нашем списке правильных ответов. Полученный код должен выглядеть так:

Продолжаем

Запустите этот код, и теперь вы должны обнаружить, что вопрос обновляется каждый раз, когда вы получаете правильный ответ! Это полноценная игра, и вы могли бы создать гораздо больше подобных проектов, используя приобретенные навыки.

Единственная небольшая проблема со всем этим заключается в том, что прямо сейчас игра вылетает, как только у вас закончатся вопросы!

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

Аргументы, переменные и условные операторы Kotlin

Теперь вы знаете, как использовать функции для группировки разделов кода в Android, и в следующей части этого руководства по Kotlin произойдет настоящее волшебство. Вы овладеете тремя бесценными навыками программирования на Kotlin и любом другом языке!

Во-первых, переменная — это «контейнер» для данных. Другими словами, переменная позволяет использовать слово для обозначения другого слова, числа или даже списка элементов. Скорее всего, вы помните переменные из уроков алгебры:

a + 2 = 3, найдите a!

Здесь, очевидно, используется для обозначения «1». Мы аналогичным образом используем переменные в программировании, чтобы у нас была возможность изменять поведение нашего кода в зависимости от взаимодействия с пользователем или других входных данных.

Что мы также можем делать с переменными, так это передавать их между функциями. Когда мы это делаем, мы называем это «аргументом». По сути, это позволяет нам передавать входные данные в нашу функцию для изменения выходных данных.

Чтобы определить аргументы, которые будет принимать функция, нам просто нужно поместить их в фигурные скобки.

Мы используем тип переменной, называемый интеджером (или целым числом), который в Kotlin называется Int. Интеджеры, как вы также можете помнить из математики, — это целые числа без десятичных знаков.

Поэтому нам нужно обновить нашу функцию, чтобы она выглядела так:

Здесь имя функции — answer (ответ), и она имеет целочисленный тип. Теперь, когда мы впоследствии вызываем нашу функцию, нам нужно добавить целое число в фигурные скобки:

Вы можете передать в функцию столько аргументов, сколько захотите, при условии, что вы определите их каждый и разделите запятыми.

В этом случае мы собираемся присвоить каждой кнопке номер. A = 1, B = 2 и C = 3. Теперь функция showToast знает, какую кнопку нажал пользователь!

Теперь нам просто нужно изменить всплывающее сообщение, которое отображается в зависимости от того, какой ответ мы читаем!

Для этого мы используем так называемый «условный оператор». Это означает, что строка кода будет работать только при определенных условиях. В этом случае мы покажем правильное сообщение, только если пользователь выбрал правильный ответ. Мы достигаем этого с помощью следующего кода:

Это инструкция if, и она покажет код внутри фигурных скобок только в том случае, если инструкция в обычных скобках имеет значение true. В этом случае, если переменная answer содержит значение 1, мы можем запустить код!

Что делать, если ответ — 2 или 3? Что ж, мы всегда можем использовать еще две условные инструкции! Но более быстрым решением было бы использовать инструкцию else. Она делает именно то, что вы ожидаете, когда следует за инструкцией if:

Поле типа OnClickListener

Вместо анонимного класса мы может определить поле с типа и сразу же его инициализировать:

private OnClickListener mButtonClickListener = new OnClickListener() {
    
    @Override
    public void onClick(View v) {
        // ... 
    }    
};

Здесь мы получает одновременное определение и реализацию. Далее созданное поле можно передать кнопке в качестве обработчика:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        
    mButton.setOnClickListener(mButtonClickListener);
}

По сравнению с анонимными классом, использовать поле типа более удобнее. Преимуществом является легкая установка обработчика сразу нескольким кнопкам и одновременное определение с реализацией. Реализация может располагаться в любом месте. Недостатком является создание поля, а именно экземпляра класса и при этом, он может иметь другие методы, но они будут не видны из вне.

Вызов нужной клавиатуры

Не во всех случаях нужна стандартная клавиатура с буковками и цифрами. Если вы пишете калькулятор, то проще показать пользователю цифровую клавиатуру. А если нужно ввести электронный адрес, то удобнее показать клавиатуру, где уже есть символ @. Ну а если ваше приложение пишется для котов, то достаточно вывести только те буквы, из которых можно составить слова Мяу и Жрать давай (к сожалению, такой клавиатуры ещё нет, но Google работает в этом направлении).

У элемента EditText на этот случай есть атрибут inputType:

В данном случае с атрибутом inputType=»textCapWords&quot каждый первый символ каждого слова при вводе текста автоматически будет преобразовываться в прописную. Удобно, не так ли?

Значение textCapSentences делает прописным каждый первый символ предложения.

Если вам нужен режим CapsLock, то используйте значение textCapCharacters и все буквы сразу будут большими при наборе.

Для набора телефонного номера используйте phone, и тогда вам будут доступны только цифры, звёздочка (*), решётка (#).

Для ввода веб-адресов удобно использовать значение textUri. В этом случае у вас появится дополнительная кнопочка .com (при долгом нажатии на нее появятся альтернативные варианты .net, .org и др.).

Вот вам целый список доступных значений (иногда различия очень трудно различимы)

text
textCapCharacters (клавиатура с символами в верхнем регистре)
textCapWords
textCapSentences
textAutoCorrect
textAutoComplete
textMultiLine
textImeMultiLine
textNoSuggestions (без подсказок при вводе текста)
textUri
textEmailAddress
textEmailSubject
textShortMessage
textLongMessage
textPersonName
textPostalAddress
textPassword
textVisiblePassword (без автокоррекции)
textWebEditText
textFilter
textPhonetic
number
numberSigned
numberDecimal
phone
datetime
date
time

SafeArgs

Иногда перед разработчиком может встать задача передачи данных между экранами приложения и для этого в Android есть удобный механизм. SafeArgs — это Gradle-плагин, позволяющий реализовать передачу данных между фрагментами.

Далее будет рассмотрено добавление SafeArgs для простейшей задачи — отображения текстового сообщения об общем числе вопросов и числе правильных ответов на .

1. Добавление зависимости SafeArgs:

Сперва необходимо добавить зависимость от библиотеки SafeArgs в Gradle-файл проекта (не модуля ).

dependencies {
    ...
    classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$version_navigation"
}

Затем необходимо включить плагин в Gradle-файл модуля .

apply plugin: 'androidx.navigation.safeargs'

После включения плагина необходимо очистить и пересобрать проект, чтобы компилятор сгенерировал «Directions»-классы, содержащие информацию о навигации между фрагментами (по типу идентификаторов ), а также позволяющие организовать передачу данных между фрагментами.

2. Использование «Directions»-классов:

После того, как «Directions»-классы сгенерированы, можно заменить использование ссылок на идентификаторы навигации на использование этих классов.

// GameFragment: Using directions to navigate to the GameWonFragment
view.findNavController().navigate(GameFragmentDirections.actionGameFragmentToGameWonFragment())
// GameFragment: Using directions to navigate to the GameOverFragment
view.findNavController().navigate(GameFragmentDirections.actionGameFragmentToGameOverFragment())

По такому же принципу выполняется замена в остальных местах.

3. Добавление аргументов в редакторе навигации:

Далее необходимо описать для данные, которые он должен получать. В данном случае — это общее число вопросом и число правильных ответов . Оба числа целочисленные ().

Для добавления описания параметров необходимо перейти в редактор навигации, выбрать и добавить аргументы в разделе «Arguments» панели «Attributes».

После этого сборка проекта будет невозможно, поскольку вызов будет требовать входных параметров, которые только что были описаны.

4. Передача данных фрагменту:

Чтобы передать данные фрагменту, необходимо передать их в вызов .

// GameFragment: Adding the parameters to the Action
view.findNavController().navigate(
    GameFragmentDirections.actionGameFragmentToGameWonFragment(numQuestions, questionIndex))

Параметры и определяют общее число вопросов и индекс текущего вопроса, а в случае с выигрышем — это и число правильных ответов.

Теперь проект успешно собирается, данные передаются, но никак не принимаются и не обрабатываются.

5. Обработка полученных данных:

Переданные в данные отобразим на экране с помощью -сообщения. Для этого необходимо воспользоваться вызовом для получения передаваемых данных.
«Args»-класс генерируется, так же как и «Directions»-классы, автоматически. Он предоставляет доступ к хранилищу куда сохраняются передаваемые данные.
Далее для получения конкретных значений необходимо обратиться к данным по именам ранее переданных переменных.

// GameWonFragment.onCreate

val args = GameWonFragmentArgs.fromBundle(requireArguments())
Toast.makeText(context, "NumCorrect: ${args.numCorrect}, NumQuestions: ${args.numQuestions}",
               Toast.LENGTH_LONG).show()

Таким образом при открытии на экране будет отображено -сообщение с полученными и .

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: