В каждом смартфоне спрятана маленькая лаборатория — набор датчиков, которые чувствуют движение, свет и даже магнитное поле. Акселерометр — один из самых доступных, он определяет наклон и ускорение устройства. Сегодня на занятии в Android Studio мы подключимся к этому датчику и напишем программу, которая будет реагировать на встряхивание телефона. Код напишем на языке Kotlin, а сам урок построен так, чтобы справился даже тот, кто впервые слышит слово «сенсор».
Что умеет акселерометр
Акселерометр измеряет ускорение по трём осям: X (влево-вправо), Y (вперёд-назад) и Z (вверх-вниз). Когда телефон лежит на столе, значения близки к нулю по X и Y, а по Z примерно равно 9.8 — это сила тяжести. Если резко дёрнуть устройство, цифры подскакивают. Именно на этом строится определение встряхивания: мы будем следить, не превысило ли ускорение заданный порог.
Подключаем SensorManager
Для работы с датчиками не нужно добавлять библиотеки — всё уже встроено в Android SDK. Главный класс — SensorManager. Получим его через системный сервис. Затем запросим сам акселерометр по типу Sensor.TYPE_ACCELEROMETER.
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
Метод getDefaultSensor возвращает ссылку на датчик или null, если такого нет. На современных телефонах акселерометр есть всегда, но проверку всё равно стоит добавить, особенно если вы пишете под старые устройства или эмуляторы.
Создаём слушатель
SensorManager получает данные не постоянно, а порциями — с заданной частотой. Чтобы ловить эти порции, нужен объект SensorEventListener с двумя методами: onSensorChanged (значения изменились) и onAccuracyChanged (точность изменилась). Нас интересует первый.
private val sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
// здесь будем проверять на встряхивание
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// не используется
}
}
Массив event.values всегда содержит три числа: ускорение по X, Y и Z. Каждое из них измеряется в метрах на секунду в квадрате.
Определяем встряхивание
Как понять, что телефон тряхнули? Простой способ — вычислить общую величину ускорения и сравнить с порогом. Если суммарный вектор превышает, скажем, 15 м/с², значит, было резкое движение.
val acceleration = Math.sqrt(
(x * x + y * y + z * z).toDouble()
)
if (acceleration > 15.0) {
// зафиксировано встряхивание
}
Можно пойти дальше и добавить задержку между срабатываниями, чтобы одно встряхивание не засчитывалось десять раз подряд. Для этого запоминаем время последнего события и игнорируем новые, пока не пройдёт, к примеру, одна секунда.
Регистрируем и отменяем слушатель
Слушатель нужно зарегистрировать, когда экран становится активным, и отключить, когда он уходит в фон. Так мы экономим батарею. Лучшее место для этого — onResume и onPause.
override fun onResume() {
super.onResume()
sensorManager.registerListener(
sensorListener,
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL
)
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(sensorListener)
}
Параметр SENSOR_DELAY_NORMAL задаёт частоту обновлений — примерно 200 миллисекунд между измерениями. Для быстрых игр можно поставить SENSOR_DELAY_GAME, а для экономии энергии — SENSOR_DELAY_UI.
Простой пример: меняем цвет экрана при тряске
Соберём всё в одном классе. Когда пользователь встряхивает телефон, фон экрана меняется на случайный цвет. Никаких дополнительных библиотек, только чистый Kotlin.
class ShakeActivity : AppCompatActivity() {
private lateinit var sensorManager: SensorManager
private var lastShakeTime = 0L
private val shakeThreshold = 15.0
private val sensorListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent?) {
if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
val acceleration = Math.sqrt((x*x + y*y + z*z).toDouble())
val now = System.currentTimeMillis()
if (acceleration > shakeThreshold && now - lastShakeTime > 1000) {
lastShakeTime = now
window.decorView.setBackgroundColor(
(0xFF000000 or Math.random().toLong() * 0xFFFFFF).toInt()
)
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
override fun onResume() {
super.onResume()
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
if (accelerometer != null) {
sensorManager.registerListener(sensorListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)
}
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(sensorListener)
}
}
Советы для начинающих
- Всегда отключайте слушатель. Если забыть
unregisterListener, датчик продолжит работать, даже когда приложение свёрнуто, и батарея сядет быстрее. - Тестируйте на реальном устройстве. Эмулятор не умеет трястись — чтобы проверить код, придётся запустить его на живом телефоне.
- Подбирайте порог под задачу. Значение 15 хорошо подходит для встряхивания в руке. Если нужно ловить лёгкие наклоны, уменьшите до 10.
Чему мы научились
Сегодняшнее занятие в Android Studio открыло для нас мир аппаратных датчиков. Мы подключили акселерометр, написали слушатель, определили встряхивание и даже сделали простую программу, меняющую цвет фона. Теперь вы знаете, как получить доступ к одному из самых популярных сенсоров, и можете экспериментировать дальше: попробуйте считать шаги, управлять игровым персонажем наклоном или создать будильник, который отключается только после хорошей встряски.