Keyboard Handler: вгору, вгору, вниз, вниз, вліво, вправо …

Оброблювач подій клавіатури KeyboardHandler використовується для виконання цілого ряду завдань Перш за все він повинен бути повязаний з View, так як від нього KeyboardHandlег отримує інформацію про події клавіатури Далі йому необхідно зберегти поточний стан кожної клавіші для предявлення цього стану в ході опитування Йому також знадобиться список екземплярів класу KeyEvent І нарешті, все це потрібно правильно синхронізувати, так як він буде приймати події з потоку для користувача інтерфейсу, які будуть оброблятися в головному ігровому циклі, запущеному в іншому потоці Досить багато роботи У Як невеликого нагадування ще раз подивимося на клас KeyEvent, який ми визначили як частина інтерфейсу введення Input:

Він просто визначає дві константи, що кодують тип клавіатурного події, а також містить три члена, в яких зберігаються тип, код клавіші і Юнікод-символ події Маючи в своєму розпорядженні цією інформацією, ми можемо реалізувати наш обробник

Лістинг 57 показує реалізацію обробника за допомогою нашого нового класу Pool і Android API, який ми обговорювали раніше

Лістинг 57 KeyboardHandlerJava: обробляє клавіатуру package combadlogicandroidgamesframeworkImp;

Клас KeyboardHandl er реалізує інтерфейс OnKeyLi stener, так що він може отримувати клавіатурні події з View Переходимо до наших елементам

Перший елемент – це масив, що містить 128 булевих значень Ми збережемо поточний стан (натиснута або не натискати) кожної клавіші в даному масиві Це стан індексується кодом клавіші Зручно, що константи androidview KeyEvent KEYCODEXXX, які задають коди клавіш, варіюються в проміжку від 0 до 127, так що ми можемо зберегти їх у формі, зручній для збирача сміття Зверніть увагу, що, на жаль, імя нашого класу KeyEvent збігається з класом KeyEvent Android, екземпляри якого будуть передаватися методом OnKeyEventListeneronKeyEventO Така плутанина виникає тільки з кодом цього обробника Оскільки навряд чи клавіатурне подію можна поіменувати краще, ніж KeyEvent, я віддаю перевагу залишити все як є

Наступний член PooI зберігає екземпляри наших класів KeyEvent Оскільки ми не хочемо перевантажувати збирач сміття роботою, будемо багаторазово використовувати всі обєкти KeyEvent, які створили

Третій елемент зберігає KeyEvent, які поки не були оброблені класом Game Кожен раз, коли ми отримуємо нову подію клавіатури в потоці користувача інтерфейсу, воно додається до цього списку

Останній елемент зберігає KeyEvent, які ми повернемо при виклику KeyboardHandl ег getKeyEvents Трохи нижче буде показано, чому ми двічі буферізуем клавіатурні події

Даний конструктор має тільки один параметр, що позначає той вид (View), від якого ми хочемо отримувати клавіатурні події Ми створюємо екземпляр класу Pool з відповідним PoolObjectFactory, реєструємо обробник як OnKeyl I stener під View і, нарешті, переконуємося, що View отримує події клавіатури, помістивши цей вид в фокус

Далі реалізуємо метод інтерфейсу OnKeyL i stener onKey, який викликається кожного разу, коли View обробляє нове клавіатурне подія Проте таким чином ігноруються всі клавіатурні події з типом KeyEvent ACTION MULTIPLE Щоб це виправити, використовуємо блок синхронізації Не забувайте, що всі події надходять з потоку для користувача інтерфейсу і потім обробляються потоком основного циклу, тому нам необхідно переконатися, що жоден з елементів не може бути паралельно доступний для декількох потоків

Усередині блоку синхронізації ми спочатку отримуємо екземпляр класу KeyEvent (нашої реалізації KeyEvent) з Pool Це допоможе нам отримати або перероблений, або новий екземпляр класу в залежності від стану Pool Далі встановлюємо елементи KeyEvent, а саме keyCode і keyChar, які беруться з KeyEvent Android, переданого в цей метод Далі декодуємо тип KeyEvent Android і встановлюємо відповідні значення для типу нашого події KeyEvent і елемента в масиві pressedKey В кінці додаємо KeyEvent в список keyEventBuf f ег, який визначили раніше

Далі переходимо до методу isKeyPres sed, який зазвичай реалізує семантику Input i sKeyPressed Ми передаємо в нього ціле число, яке відповідає коду клавіші (однієї з констант KeyEventKEYCODE XXX Android) і повертаємо інформацію про те, натиснута дана клавіша чи ні Статус клавіші можна отримати з масиву pressedKey, попередньо перевіривши межі діапазону Зверніть увагу, що ми встановили елементи даного масиву в минулому методі, що викликається в потоці користувача інтерфейсу Оскільки ми знову працюємо з найпростішими типами, необхідності в синхронізації немає

Останній метод нашого обробника називається getKeyEvents Він реалізує семантику методу Input getKeyEvents Ми знову запускаємо блок синхронізації, так як даний метод викликається з іншого потоку

Тепер починаються цікаве ™ Ми проходимо в циклі по масиву keyEvents і вставляємо всі KeyEvent, що зберігаються в ньому, в наш Pool Памятаєте, як ми отримували екземпляри KeyEvent з класу Pool в методі опКеуО в потоці користувача інтерфейсу Тут ми вставляємо їх назад в Pool Але хіба список KeyEvent не порожній Даний список буде порожній тільки під час першого виклику даного методу Щоб зрозуміти, чому виникає така ситуація, необхідно вивчити даний метод до кінця

Після нашого загадкового циклу вставки в Pool ми очищаємо список KeyEvent і заповнюємо його подіями зі списку keyEventsBuf fer І нарешті, очищаємо список keyEventsBuffer і повертаємо свежезаполненний список KeyEvent викликає стороні Що ж відбувається тут

Найпростіше це пояснюється наступним простим прикладом Ми перевіряємо, що відбувається зі списками KeyEvent і keyEventsBuffer, а також з Pool кожен раз, коли нове подія зявляється в потоці користувальницького інтерфейсу або коли гра запрошувати нову подію з основного потоку:

1 Спочатку отримуємо нову подію в потоці користувача інтерфейсу У Pool поки нічого немає, тому створюється новий екземпляр класу KeyEvent (KeyEventl) Він міститься у список keyEventsBuffer

2 Викликаємо getKeyEvents в основному потоці Він використовує KeyEventl зі списку keyEventsBuffer і поміщає його в список keyEvent, який повертається викликає стороні

3 Отримуємо ще одна подія в потоці UI У нашому Pool раніше нічого немає, тому створюється ще один екземпляр класу KeyEvent (KeyEvent2) Він також міститься у список keyEventsBuffer

4 Основний потік знову викликає getKeyEvents Здесначінается найцікавіше При вході в даний метод список KeyEvent раніше містить KeyEventl Цикл вставки помістить дана подія в наш Pool Потім він очистить список KeyEvent і вставить другий KeyEvent в keyEventsBuffer У нашому випадку це KeyEvent2 Ми тільки що переробили клавіатурне подія

5 Нарешті, отримуємо ще одна подія в потоці UI На цей раз у нас є вільний KeyEvent в Pool, який ми, зрозуміло, використовуємо ще один раз І ніяких збирачів сміття

Однак у даного механізму є серйозний недолік: нам доведеться досить часто викликати KeyboardHandlergetKeyEvents, інакше список швидко заповниться і не можна буде повернути обєкти в Pool Однак якщо ми будемо памятати про це, все має бути гаразд

Оброблювачі торкань

А ось і наслідки поділу на старі і нові версії

Всі константи, які ми використовували в коді мультитач (наприклад, MotionEventACTI0N P0INTER ID MASK), недоступні у версіях Android 15 або 16 Ми цілком можемо застосовувати їх у нашому коді, якщо нашою кінцевою метою є запуск програми на тій версії Android, що підтримує даний API Однак тоді наш додаток не буде працювати на пристроях, на яких встановлена ​​версія Android 15 або 16 Але ж ми з вами хочемо, щоб наша гра була доступна на всіх версіях Android Як же нам вирішити дану проблему

Для цього скористаємося невеликою хитрістю Напишемо два обробника, один з яких буде використовувати API Android 15 для обробки одиночних торкань (single-touch), а інший – мультитач API Android 20 і вище До тих пір поки ми не будемо використовувати мультитач-обробник на пристроях з версією Android нижче 20, ми можемо бути абсолютно спокійні Віртуальна машина не буде завантажувати код, тому можна не хвилюватися про те, що зявляться винятку Все, що нам потрібно, – зясувати, яка версія Android встановлена ​​на пристрої, і вибрати для неї відповідний обробник Ви побачите, як це працює, коли ми будемо обговорювати клас Androidlnput А зараз сконцентруємося на наших двох обробниках

Джерело: Mario Zechner / Маріо Цехнер, «Програмування ігор під Android», пров Єгор Сидорович, Євген зазноби, Видавництво «Пітер»

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*