Підтримка пристроїв введення – РОЗРОБКА ІГОР ДЛЯ ОС ANDROID

Як обговорювалося в Android ми отримуємо інформацію від різних пристроїв введення У цьому розділі ми обговоримо три найбільш важливих методу введення і роботу з ними: сенсорний екран, клавіатуру і акселерометр

Обробка подій торкання і множинних дотиків

Сенсорний екран – ймовірно, найважливіший спосіб отримання даних від користувача До версій Android 20 API підтримував обробку тільки одиночних торкань Мультитач був представлений в Android 20 (версія SDK 5) Його обробка була вбудована в API для одиночних торкань, що дало неоднозначні результати з точки зору зручності Для початку ми вивчимо обробку одиночних торкань, доступну у всіх версіях Android

Обробка одиночних торкань Коли ми обробляли натискання кнопки, то бачили, що інтерфейси слухача є тим інструментом, за допомогою якого Android повідомляє нам про настання подій Те ж саме і з дотиками – ці події передаються реалізації інтерфейсу OnTouchLi stener, зареєстрованого нами з Vi ew Інтерфейс OnTouchLi stener включає в себе лише один метод:

Перший аргумент – обєкт Vi ew, до якого відносяться події торкання Другий аргумент – це те, що нам необхідно розібрати для отримання цієї події

OnTouchLi stener може бути зареєстрований в будь реалізації Vi ew за допомогою методу View setOnTouchLi stenerC) і буде викликатися перед тим, як MotionEvent буде відправлений до View Ми можемо повідомити View в нашій реалізації методу onTouch, що подія торкання вже оброблено, повертаючи з методу true При поверненні значення f al se обєкт Vi ew сам буде обробляти цю подію Примірник MotionEvent володіє декількома методами, нам цікаві три з них

MotionEventgetX і MotionEventgetY – повертають координати х і у точки дотику всередині View Як ви вже знаєте, координати визначаються від лівого верхнього кута – зліва направо по осі х і зверху вниз по осі у Значення координат повертаються у точках При цьому тип повертаються цими методами значень – f 1oat, тому координати мають субпіксельних точність

Moti onEvent getActi on – повертає тип події торкання Це цілочисельне значення, відповідне одному з елементів списку Moti onEvent ACTI0N D0WN, MotionEventACTI0N M0VE, MotionEventACTION CANCEL і MotionEventACTIONJJP

Звучить досить просто, і це враження що оманливе Подія Moti onEvent ACTI0ND0WN виникає, коли палець стосується екрана При русі пальцем по дисплею виникають події Moti onEvent ACTI0N M0VE Зверніть увагу – ви завжди будете отримувати події Moti onEvent ACTI0N M0VE, оскільки не зможете тримати палець нерухомо, щоб їх уникнути

При піднятті пальця від екрану спрацьовує подія Moti onEvent ACTIONJJP

Події Moti onEvent ACTION CANCEL трохи більше загадкові У документації йдеться, що вони виникають при скасуванні поточного жесту Але в реальній роботі я ніколи не спостерігав виникнення цієї події Тим Проте ми будемо обробляти і його (уявивши, що це подія Moti onEvent ACTIONJJP) при створенні нашої першої гри

Створимо просту тестову активність і подивимося, як це працює в коді Активність повинна показувати поточну позицію пальця на дисплеї, а також тип початку події Лістинг 43 демонструє, що я маю на увазі

Лістинг 43 SingleTouchTestjava тестування обробки одиночних торкань package combadogi сandroi dgames

Наша активність реалізує інтерфейс OnTouchLi stener У ній також є два члена, один для TextVi ew, другий – StringBui 1 der, який ми будемо використовувати для створення рядків для подій

Метод onCreate не вимагає довгих пояснень Єдине нововведення – виклик TextViewsetOnTouchListener, в якому ми реєструємо нашу активність в TextVi ew, щоб вона могла отримувати MotionEvents

Все, що залишилося, – власне реалізація методу onTouchC) Ми ігноруємо аргумент типу View, тому що знаємо, що це наш обєкт TextVi ew Нас цікавить отримання типу події, додавання його до обєкта Stri ngBui 1 der, отримання координат і оновлення тексту TextVi ew Ось і все Крім того, ми записуємо повідомлення в LogCat, щоб бачити порядок появи подій – адже в TextVi ew ми побачимо тільки Останнім оброблене подія (StringBuider очищається при кожному виклику onTouchC)

Остання невелика подробиця про метод onTouch – вираз return, в якому ми повертаємо true Зазвичай ми дотримуємося концепції слухача і повертаємо fal se, щоб не звязуватися зайвий раз з процесом обробки подій Якщо ми зробимо так само в нашому прикладі, то не побачимо ніяких подій, крім Moti onEvent ACTI0N D0WN Тому ми повідомляємо TextVi ew, що ми тільки що використовували подія У різних реалізаціях View поведінка може відрізнятися На щастя, в нашій е будуть застосовуватися ще всього три види View, і ми зможемо використовувати будь-яку подію за нашим бажанням

Якщо ми запустимо цей додаток на емуляторі або підключеному пристрої, то побачимо, що TextVi ew завжди показує тип останньої події і координати, обчислені в методі onTouch Крім того, всі ці повідомлення ви можете побачити в LogCat

Я не визначав явно орієнтацію активності у файлі маніфесту Якщо ви повернете пристрій так, щоб активність перейшла в ландшафтний режим, то координатна система, звичайно, зміниться Малюнок 46 показує активність в портретному і ландшафтному режимах В обох випадках я постарався стосуватися екрана в середині Зверніть увагу – координати х і у начебто помінялися місцями На малюнок також додані осі і точка дотику В обох випадках координати починаються в лівому верхньому кутку TextVi ew, вісь х рухається вправо, вісь у – вниз

Рис 46 Дотик екрану в портретному (ліворуч) і ландшафтному (праворуч) режимах

Залежно від орієнтації змінюються максимально можливі значення х і у Для прикладу ми використовували Nexus One (дозвіл 480 х 800 пікселів в портретному режимі і 800 х 480 в ландшафтному) Оскільки координати дотику дано щодо View (яке займає не весь екран пристрою), максимальне значення координати х буде завжди менше дозволу по висоті Пізніше ви довідаєтеся, як включити повноекранний режим, щоб прибрати рядок стану і заголовок

На жаль, існують деякі проблеми з подіями торкання на старих версіях Android і пристроях першого покоління

Лавина подій торкання Драйвер повідомляє про максимально можливій кількості подій, коли палець знаходиться на екрані, – на деяких пристроях воно вимірюється сотнями за секунду Ми можемо частково впоратися з цією проблемою, помістивши виклик Thread sleep (16) в метод onTouchC) – після кожної події потік обробки буде припинятися на 16 мілісекунд У цьому випадку ми отримаємо максимально 60 подій в секунду, чого більш ніж достатньо для нормального ігрового процесу Ця проблема стосується тільки пристроїв на базі Android 15

Дотик екрану завантажує процесор Навіть включивши паузу в методі onTouch, ми повинні розуміти, що система обробляє процеси в своєму ядрі На старих пристроях (наприклад, Нього або G1) ці дії можуть захоплювати до 50% потужності процесора, що залишає менше простору для потоку головного циклу Як наслідок, падає частота кадрів – іноді до такого ступеня, коли ігровий процес стає неможливим На пристроях другого покоління проблема зустрічається набагато рідше, і про неї можна забути На жаль, для старих апаратів якогось універсального рішення не існує

Загалом, для впевненості варто поміщати виклик Threadsiеер (16) у всі ваші методи onTouchO На нових пристроях це не дасть ніякого ефекту на більш давніх це принаймні вбереже вас від лавини повідомлень про торканнях

Враховуючи, що апарати першого покоління все ж поступово йдуть з ринку, проблема стає менш значущою з плином часу Однак вона як і раніше може засмучувати розробників ігор Спробуйте виправдатися перед користувачами тим, що ваша гра працює як черепаха тому, що якийсь драйвер витрачає всі ресурси процесора Результат цілком передбачуваний – користувачів це не буде хвилювати

Обробка множинних дотиків Це були квіточки – прийшов час головною головного болю API для роботи з мультитач було вбудовано в клас MotionEvent, спочатку призначений для обробки окремих торкань Тому при спробі обробки множинних дотиків виникають деякі непорозуміння Спробуємо їх вирішити

ПРИМІТКА

API для мультитач, схоже, викликає деякі запитання навіть у інженерів Android, що створили його Він був сильно модернізований в SDK версії 8 (Android 22) – нові методи, нові і навіть перейменовані константи Ці зміни повинні зробити роботу з множинними дотиками трохи простіше, але доступні вони тільки починаючи з SDK версії 8 Для підтримки всіх версій Android з мультитач (починаючи з 20) доведеться використовувати SDK версії 5

Обробка множинних дотиків досить сильно схожа на обробку подій однократного торкання Ми реалізуємо той же інтерфейс OnTouchLi stener, що і для одноразових дотиків Нам також необхідний екземпляр MotionEvent, з якого будуть зчитуватися дані Ми будемо обробляти ті ж події, що і раніше (наприклад, Moti onEvent ACTIONJJP), і додамо до них ще декілька

Покажчики ID і індекси Відмінності починаються, коли нам необхідний доступ ккоордінатам торкання MotionEvent getX і Moti onEvent getY повертають координати одного пальця на екрані При обробці подій множинних дотиків нам необхідно використовувати перевантажені версії цих методів для отримання так званого номера покажчика Це має виглядати так:

Тепер ми можемо очікувати, що poi nterlndex безпосередньо повязаний з одним з пальців, що стосуються екрану (наприклад, перший палець на екрані отримує poi nterlndex, рівний 0, наступний – 1 і т д) На жаль, це не так

poi nterlndex – номер у внутрішніх масивах Moti onEvent, що зберігають координати події для певного пальця, який стосується дисплея Реальний ідентифікатор пальця називається ідентифікатором покажчика Існує окремий метод MotionEventgetPointerldentifier (int pointerlndex), який повертає ідентифікатор покажчика, що базується на номері покажчика Ідентифікатор покажчика для пальця не зміниться, поки той не відірветься від екрану (але це не обовязково вірно для номера покажчика)

Розглянемо, як ми можемо отримати номер покажчика для події Поки проігноруємо тип події

Ви, мабуть, подумали зараз те ж, що подумав я, коли вперше написав цей код Однак перш, ніж остаточно втратити віру в людство, спробуємо розшифрувати, що тут відбувається Ми отримуємо тип події з Moti onEvent через MotionEvent getAction Відмінно, це ми вже освоїли Далі ми виконуємо бітову операцію AND, використовуючи отримане від методу Moti onEvent getActionO значення і константу Moti onEvent Тепер починається найвеселіше

Константа має значення OxffOO, тому ми, по суті, робимо всі біти рівними 0, крім бітів з 8 по 15, що зберігають номер покажчика події Нижні 8 біт числа, повернутого методом event get Act i on , Зберігають значення типу події (наприклад, MotionEventACTION JD0WN і т д) Цією бітової операцією ми, простіше кажучи, стираємо дані про тип події

Тепер зрушення набуває більший сенс Ми здійснюємо його за допомогою константи MotionEvent ACTION POIIMTER ID SH I FT (рівний 8), тобто переміщаємо біти з 8 по 15 в біти з 0 по 7, отримуючи актуальний номер покажчика на подію Зверніть увагу – наші чарівні константи називаються XXX POINTER ID XXX, а не XXX POINTER INDEX XXX (що мало б більше сенсу – адже нам необхідний номер покажчика, а не його ідентифікатор) Що ж, інженери Android теж помиляються У SDK версії 8 вони прибрали ці константи і представили натомість нові, названі XXX POINTER INDEX XXX і мають ті ж значення, що і прибрані Для забезпечення роботи старих програм, написаних на SDK 5, старі константи теж збережені

Отже, ми знаємо, як отримати цей загадковий номер покажчика, з яким ми можемо запросити координати і ідентифікатор покажчика події

Маска операції та інші типи подій Далі нам необхідно отримати справжній тип події мінус додатковий номер покажчика, закодований в числі, повернутому MotionEvent GetActionC) Нам лише необхідно виключити номер покажчика:

Так, це було просто На жаль, ви зрозумієте це, тільки якщо знаєте, що це номер покажчика та що він зашифрований у дії

Залишилося декодувати тип події, як ми робили це раніше Я вже згадував, що у нас зявилися нові типи подій, тому розглянемо їх

MotionEvent ACT 10N P01 NTER D0WN – виникає для кожного пальця (починаючи з другого), який стосується екрану Перший палець все ще викликає подія Moti onEvent ACTI0N D0WN

MotionEvent ACT 10N P01NTE RUP – аналог попередньої дії Подія виникає, коли палець відривається від екрану і при цьому екрану стосуються більше одного пальця Останній палець при відриві викликає подія Moti onEventACTIONJJP Це не обовязково буде перший палець, що досягне екрану

На щастя, ми можемо уявити, що два цих нових типи подій нічим не відрізняються від старих добрих Moti onEvent ACTIONJJP і Moti onEvent ACTI0N D0WN

Остання відмінність – той факт, що один MotionEvent може володіти даними для безлічі подій Так, ви все вірно прочитали З цієї причини зєднані разом події повинні мати однаковий тип У реальності це відбувається тільки для події MotionEvent ACTION J10VE, і нам доводиться миритися з цим фактом Щоб перевірити, скільки подій міститься в одному Moti onEvent, ми використовуємо метод Moti onEvent getPointerCountO, що повідомляє нам про кількість пальців, що стосуються нашого дисплея Далі ми можемо отримати ідентифікатор покажчика і координати для номерів покажчика від Про до MotionEventgetPointerCountO – За допомогою методів Moti onEvent getX, MotionEvent getY і Moti onEvent getPointerlcK)

На практиці

Напишемо приклад для даного API Нам хотілося відслідковувати в ньому торкання всіх 10 пальців (на даний момент не існує пристроїв, здатних відстежувати більша їх кількість, так що нам нічого не загрожує) Android присвоїть цим пальцях ідентифікатори покажчиків від 0 до 9 в тому порядку, в якому вони стосуються дисплея Отже, ми будемо зберігати координати кожного ідентифікатора і його стан (стосується чи ні), а також виводити цю інформацію на екран за допомогою TextVi ew Назвемо нашу тестову активність MultiTouchTest Повний код показаний в лістингу 44

Лістинг 44 MultiTouchTestJava тестуємо Multitouch API package combad ogi сandroi dgames

Ми реалізували інтерфейс OnTouchListener, як і раніше Для відстеження координат та стану торкання 10 пальців ми додали три члена-масиву, в яких буде зберігатися потрібна нам інформація Масиви хіу зберігають координати кожного ID покажчика, масив touched містить дані про те, чи стосується даний ID екрану чи ні

Далі я створив невеликий допоміжний метод, що виводить в TextVi ew поточний стан пальців Він просто пробігає по всіх 10 станам пальців і склеює їх в один рядок за допомогою Stri ngBui 1 der Кінцевий текст виводиться у TextView

Метод onCreate встановлює нашу активність і реєструє її як OnTouchLi stener в TextVi ew Ця частина коду вам вже повинна бути знайома

Тепер найважча частина – метод onTouchO Починаємо з отримання типу події на основі цілого числа, повернутого методом event getActi on Далі отримуємо номер покажчика і відповідний йому ідентифікатор з Moti onEvent, як вже обговорювалося раніше Ядро методу onTouch – велике плутане вираз switch, яке ми вже використали в усіченому вигляді при обробці одиночних торкань Ми групуємо події в три високорівневі категорії

Відбулося торкання екрана (MotionEventACTI0N D0WN, MotionEventACTI0N P0NTER DOWN) Для таких ідентифікаторів покажчиків ми встановлюємо стан торкання в true, а також зберігаємо поточні координати цього покажчика

Стався відрив покажчика від екрану (MotionEventACTIONJJP, MotionEvent ACTI0N P0INTER UP, Moti onEvent CANCEL) Стан торкання для таких ідентифікаторів встановлюється в false, зберігаються їхні останні відомі координати

Один або кілька пальців рухаються по екрану (MotionEventACTI0N M0VE) Ми перевіряємо кількість подій, що знаходяться в даний момент в Moti onEvent, і потім оновлюємо координати для номерів покажчиків від 0 до MotionEvent getPointerCountO – 1 Для кожної події ми отримуємо відповідний ідентифікатор покажчика та оновлюємо його координати

При виконанні події ми оновлюємо TextVi ew за допомогою виклику методу updateView, визначеного нами раніше Нарешті, повертаємо значення true – знак того, що ми обробили подія торкання

Малюнок 47 демонструє висновок активності після того, як я торкнувся екрана мого Nexus One двома пальцями, а потім трохи поворушив ними

Рис 47 Результат використання мультитач

Залишилося кілька питань, які варто обговорити при запуску цього прикладу

Якщо ми запустимо його на емуляторі з версією Android нижче 20, то отримаємо жахливе виняток, оскільки ми використовуємо відсутнє в старих версіях API З цим можна боротися, визначаючи в процесі роботи додатки поточну версію Android і застосовуючи у випадку з Android 15 і 16 API для обробки одиночних торкань, а у випадку з Android 20 і пізніше – мультитач API

На емуляторі немає підтримки мультитач Ми можемо створити його з використанням версії Android 20 і вище, але це нічого не дасть – миша-то у нас всього одна І навіть якби їх було дві, це нічого б не змінило

Торкніться двома пальцями дисплея, підійміть першим і знову торкніться їм екрана Другий палець збереже свій ідентифікатор торкання після того, як перший палець піднімається Коли перший палець знову стосується дисплея, він отримує перший вільний ідентифікатор покажчика (у даному випадку 0) Кожен новий палець, що стосується екрану, отримує перший незайнятий ідентифікатор Це правило, яке варто запамятати

Якщо ви протестуєте даний приклад на Nexus One або Droid, то можете звернути увагу на кілька дивна поведінка при перетині двох пальців на одній осі Це відбувається через те, що екрани даних пристроїв не підтримують повною мірою відстеження окремих пальців Це насправді велика проблема, але ми з нею впораємося, розробляючи наш інтерфейс з деякою оглядкою

Поки просто тримайте це в голові

Отже, ось таким чином здійснюється обробка множинних дотиків на Android Складно, звичайно, але, розібравшись один раз і примирившись з деякою незграбністю реалізації, цей функціонал цілком можна використовувати

ПРИМІТКА

Вибачте, що підірвав вам мозок Цей пункт і був справді важким На жаль, офіційна документація для цього API неймовірно лаконічна, і більшість людей вивчають ці можливості, керуємося API на практиці Я пропоную вам попрацювати з вищенаведеним кодом, поки ви не будете розуміти точно все, що в ньому робиться

Джерело: 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>

*

*