Россия, Новосибирск |
Разработка приложений для планшетных компьютеров
9.1. Введение
Последнее время наряду с ноутбуками и смартфонами набирает популярность еще одна разновидность мобильных компьютеров - планшетные. Наиболее известным примером такого компьютера является Apple iPad. От ноутбуков планшеты отличает отсутствие традиционной клавиатуры, от смартфонов - большие размеры. Они ориентированы в первую очередь на развлечение пользователя, пассивное восприятие информации. В линейке аппаратных платформ для ОС Android планшеты занимают одно из главных мест.
В этой лекции мы рассмотрим аппаратные особенности планшетов с точки зрения прикладного программиста. Во-первых, это сенсорный экран, который в планшете заменяет почти все традиционные устройства ввода, такие как "мышь" и клавиатура. В случае "мыши" эти замена почти эквивалента по природе самого сенсорного экрана - оба они являются pointing device, и мы сосредоточимся как раз на этом случае.
Во-вторых, планшет обладает набором датчиков, например, датчиком ориентации, освещения, акселерометром и т.п. Для поддержания такого разнородного набора датчиков требуется самостоятельный API. Датчики, в свою очередь, позволяют реализовать более удобный, более интуитивный интерфейс управления программой на планшете, который был бы невозможен на нетбуке.
Кроме аппаратных особенностей, у планшетов есть особенности пользовательского интерфейса, для которого и исчезновение клавиатуры, и изменение размера экрана критичны. Эти особенности также являются очень существенными, но мы их касаться не будем.
9.2. Датчики
Планшетные компьютеры, как и многие другие мобильные устройства обладают набором датчиков, например, датчиком ориентации, освещения, акселерометром и т.п. Для поддержания такого разнородного набора датчиков требуется самостоятельный API. Датчики, в свою очередь, позволяют реализовать более удобный, более интуитивный интерфейс управления программой на планшете, который был бы невозможен на нетбуке.
Датчики - неотъемлемая часть современного планшета или смартфона. В продвинутых телефонах они появились очень давно - например, датчик, позволяющий телефону определить, приложен ли он к уху или должен работать в режиме громкой связи. Сейчас количество датчиков и их возможности существенно возросли. Это касается как датчиков высокого уровня (получение текущей ориентации экрана (портрет, пейзаж)), так и низкого уровня, как, например, получение в режиме реального времени показаний акселерометра.
Вместе с тем набор датчиков меняется от устройства к устройству, более того, трудно предугадать, какие новые датчики появятся в будущем. Данные, получаемые от датчиков, неоднородны. Эти ограничения делают API датчиков относительно сложным.
9.3. Сенсорные возможности Android
Один из приятных аспектов работы с платформой Android заключается в возможности получить доступ к некоторым полезным компонентам самого устройства. До сих пор разработчиков мобильных устройств разочаровывала невозможность доступа к их внутреннему оборудованию. Хотя между вами и металлом все же остается прослойка Java-среды Android, команда разработчиков Android вывела многие возможности аппаратуры на поверхность. А так как Android - платформа с открытым исходным кодом, то можно засучить рукава и написать собственный код для решения своих задач. В Android существует много различных датчиков, используя которые, разработчики могут создавать интересные и полезные программные решения.
9.4. Интерфейс традиционных датчиков в Android API
Рассмотрим более подробно работу с датчиками в Android. Пакет android.hardware представляет разработчику API, который может быть использован при необходимости в приложениях, опирающихся на аппаратные возможности устройства. К примеру, пакет представляет интерфейс управления камерой и другими датчиками устройства. Операционная система Android по умолчанию представляет программную абстракцию любого физического элемента устройства, будь то камера или датчик движения, но программируя приложения для работы с датчиками устройства, необходимо первоначально убедится, что требуемый датчик присутствует в мобильном устройстве. Чтобы обезопасить себя от таких ошибок нужно в манифест-файле программы использовать директиву <uses-feature>.
В табл. 9.1 приведено описание аппаратно-ориентированных интерфейсов Android.
Camera.AutoFocusCallback | Интерфейс, осведомляющий об окончании автофокусировки камеры. Регистрирует событие на программном уровне ОС. |
Camera.ErrorCallback | Интерфейс, осведомляющий об ошибках. Регистрирует событие на программном уровне ОС. |
Camera.FaceDetectionListener | Интерфейс, распознающий лицо в предварительном просмотре. Регистрирует событие на программном уровне ОС. |
Camera.OnZoomChangeListener | Интерфейс, осведомляющий об изменениях зума камеры. Регистрирует событие на программном уровне ОС. |
Camera.PictureCallback | Интерфейс обратного вызова, используется для подачи данных после съемки. |
Camera.PreviewCallback | Интерфейс обратного вызова используются, чтобы предоставить копии предварительного просмотра кадров как они отображаются на устройстве. |
Camera.ShutterCallback | Интерфейс обратного вызова используется для обозначения момента фактического захвата изображения. |
SensorEventListener | Интерфейс используется для получения уведомлений от менеджера датчиков (SensorManager), в тот момент, когда значение датчика изменилось. |
SensorListener | Интерфейс реализован с помощью класса, который используется для вывода значений датчиков по мере их изменения в режиме реального времени. Приложение реализует этот интерфейс для мониторинга одного или нескольких имеющихся аппаратных датчиков. |
Camera | Камера класс используется для установки настройки захвата изображения, старт/стоп предварительного просмотра, фотографии и извлечения кадров для кодирования видео. |
Sensor | Класс представляет датчик. |
SensorEvent | Класс представляет события датчика, а также содержит полезную информацию такую, как тип сенсора, временная метка, точность и данные сенсора. |
SensorManager | Класс, обеспечивающий доступ к внутренним датчикам платформы Android. |
Пакет android.os.* | Пакет, содержащий несколько полезных классов для взаимодействия с операционной средой, включая управление питанием, поиск файлов, обработчик и классы для обмена сообщениями. Как и многие другие портативные устройства, телефоны на базе Android могут потреблять достаточно много электроэнергии. Обеспечение "бодрствования" устройства в нужный момент, чтобы проконтролировать нужное событие, - важный аспект проектирования, заслуживающий особого внимания. |
Рассмотрим более подробно несколько классов и интерфейсов.
SensorEventLstener. Используется для получения уведомлений от SensorManager, в тот момент, когда показания датчика меняются. Содержит описание двух основных методов, которые необходимо описать в реализующем классе.
- 1) public abstract void onAccuracyChanged (Sensor sensor, int accuracy);
Вызывается тогда, когда точность показаний датчика изменяется. Параметр sensor определяет датчик - объект класса Sensor. Параметр accuracy определяет точность измерений датчика. Точность может быть: высокая, низкая, средняя, ненадежные данные.
- 2) public abstract void onSensorChanged (SensorEvent event, float values[]);
Метод вызывается всякий раз, когда изменяется значение датчика. Этот метод вызывается только для датчиков контролируемых самим приложением. В число аргументов метода входит целое, которое указывает, что значение датчика изменилось, и массив значений с плавающей запятой, отражающих собственно значение датчика. Некоторые датчики выдают только одно значение данных, тогда как другие предоставляют три значения с плавающей запятой. Датчики ориентации и акселерометр дают по три значения данных каждый.
SensorManager. Предоставляет доступ к различным датчикам устройства. Используя метод getSystemService с параметром SENSOR_SERVICE, можно получить экземпляр класса. Программируя приложения использующие датчики всегда необходимо убедиться в том, что датчики не функционирует, когда приложение приостановлено.
Пример демонстрирует основы работы с классом SensorManager и Sensor. Объект интересующего датчика может быть получен при помощи вызова метода, объекта класса SensorManager, getDefaultSensor(Sensor.<тип>). Все типы датчиков Android описаны в виде констант в классе Sensor. Методы onResume() и onPause() необходимы для экономичного использования энергоресурсов устройства. Всякий раз, когда приложение приостанавливает свою работу, сбрасывайте слушатель датчика. Для взаимодействия с датчиком приложение должно зарегистрироваться на прием действий, связанных с одним или несколькими датчиками. Регистрация осуществляется с помощью метода SensorManager.registerListener().
SensorEvent. Класс описывает различные типы событий датчиков, которые вычисляются устройством по-разному в зависимости от разновидности датчика. Будучи шаблоном, описывающим процесс функционирования различных датчиков (ускорения, ориентации…), класс представляет очень гибкое средство получения показаний от этих компонентов устройства. Рассмотрим более подробно структуру класса.
Класс имеет четыре основных поля, раскрывающих процесс взаимодействия датчика устройства с окружающим миром.
- Accuracy - поле определяет точность показаний сенсора, обычно значение этой величины - константа, которая задается при регистрации слушателя на объект класса Sensor.
- Sensor - объект сгенерировавший событие.
- Timestamp - величина типа long, сообщающая время (в наносекундах) возникновения события.
- Float values[] - значения с датчика, которые отображают процесс взаимодействия устройства с окружающим миром. Этот параметр зависит от типа датчика.
public class SensorActivity extends Activity, implements SensorEventListener { private final SensorManager mSensorManager; private final Sensor mAccelerometer; public SensorActivity() { mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor (Sensor.TYPE_ACCELEROMETER); } protected void onResume() { super.onResume(); mSensorManager.registerListener(this,mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); } protected void onPause() { super.onPause(); mSensorManager.unregisterListener(this); } public void onAccuracyChanged(Sensor sensor, int accuracy) { } public void onSensorChanged(SensorEvent event) { } }