Содержание
Показания датчиков устройства Android представлены различными типами данных — векторами типа Vector3
, простыми числами типа double
и так далее. В этой части мы допишем наше приложение по работе с датчиками и научимся считывать их показания.
Расположение осей координат относительно устройства
Датчики, возвращающие данные по трем осям, например, акселерометр или гироскоп используют систему координат показанную на рисунке ниже:
то есть, ось X направлена вправо, ось Y — вверх, а ось Z — вперед (на нас). Координаты по оси Z за экраном устройства имеют отрицательное значение.
Событие ReadingChanged
Все рассматриваемые нами классы датчиков предоставляют событие ReadingChanged
, которое генерируется каждый раз при чтении данных. При этом, каждый датчик устройства возвращает свои показания в аргументах события, а именно в свойстве Reading
аргументов события. Ниже в таблице показаны типы данных, возвращаемых в свойстве Reading
каждого типа датчика:
Интерфейс датчика | Тип данных датчика | Описание |
IAccelerometer |
AccelerometerData |
Ускорение по осям X, Y, Z. |
IBarometer |
BarometerData |
Текущее давление в гектопаскалях |
ICompass |
CompassData |
Магнитный северный заголовок устройства. Магнитный северный заголовок устройства — это направление, в котором указывает устройство, ориентированное на магнитный север. |
IGyroscope |
GyroscopeData |
Угловая скорость по осям X,Y,Z. Единицы измерения — радианы в секунду. |
IMagnetometer |
MagnetometerData |
Вектор магнитного поля. Единицы измерения по осям — микротесла |
IOrientationSensor |
OrientationSensorData |
Показания IOrientationSensor возвращаются в виде Quaternion — описания ориентации устройства в двух трехмерных системах координат:
Первая система координат — система координат устройства (см. рисунок выше) Вторая система координат — трехмерная система координат относительно Земли имеет следующие оси:
|
Показания датчиков устройства
Подписка на событие ReadingChanged
Чтобы получить показания датчика, нам необходимо подписаться на событие ReadingChanged
. Так как для каждого датчика используются свои аргументы, а сам процесс подписки/отписки будет идентичным, то продемонстрируем этот процесс на примере одного вида датчика, например, акселерометра. Оставшиеся типы датчиков и получение их данных будет показано в конце этой части.
Итак, откроем код модели представления SensorsViewModel и добавим в неё обработчик события ReadingChanged
для акселерометра:
public AccelerometerData AccelerometerData { get; private set; } public class SensorsViewModel : INotifyPropertyChanged { ... private void Accelerometer_ReadingChanged(object? sender, AccelerometerChangedEventArgs e) { if (AccelerometerData != e.Reading) { AccelerometerData = e.Reading; OnPropertyChanged("AccelerometerData"); } } ... }
здесь AccelerometerData
— свойство в котором мы храним данные, полученные от датчика. Теперь добавим два метода первый из которых будет подписываться на событие конкретного датчика, а второй, наоборот — отписываться от события:
private void SubscribeToEvent(object sensor) { switch (sensor) { case IAccelerometer: { (sensor as IAccelerometer).ReadingChanged += Accelerometer_ReadingChanged; break; } //тут добавим другие типы датчиков } } private void UnsubscribeFromEvent(object sensor) { switch (sensor) { case IAccelerometer: { (sensor as IAccelerometer).ReadingChanged -= Accelerometer_ReadingChanged; break; } //тут добавим другие типы датчиков } }
Так как все типы датчиков у нас хранятся в виде object
, то мы проверяем тип объекта датчика и выбираем какой обработчик выбрать для подписки/отписки. Теперь применим эти методы в команде StartCommand
следующим образом:
public SensorsViewModel() { StartCommand = new Command<SensorInfo>((sensorInfo) => { if (sensorInfo.CanStart) { if (sensorInfo.IsStarted == false) { SubscribeToEvent(sensorInfo.Sensor); var method = sensorInfo.Sensor.GetType().GetMethod(name: "Start", genericParameterCount: 0, [typeof(SensorSpeed)]); var result = method?.Invoke(sensorInfo.Sensor, [SensorSpeed.Default]); } else { UnsubscribeFromEvent(sensorInfo.Sensor); var method = sensorInfo.Sensor.GetType().GetMethod(name: "Stop"); var result = method?.Invoke(sensorInfo.Sensor, null); } } sensorInfo.IsStarted = (bool)sensorInfo.Sensor.GetType().GetProperty("IsMonitoring").GetValue(sensorInfo.Sensor); });
Если датчик есть на устройстве (CanStart == true
) и он не запущен (sensorInfo.IsStarted == false
), то мы подписываемся на событие и запускаем датчик, иначе — отписываемся от события и останавливаем работу датчика.
Остается только применить новый код в приложении. Изменим код XAML страницы MainPage следующим образом:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiSensors.MainPage" xmlns:local="clr-namespace:MauiSensors.ViewModels" xmlns:converters="clr-namespace:MauiSensors.Converters" xmlns:models="clr-namespace:MauiSensors.Models" x:DataType="local:SensorsViewModel"> .... <ScrollView> <VerticalStackLayout Padding="30,0" Spacing="25"> .... <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"></ColumnDefinition> <ColumnDefinition Width="1.5*"></ColumnDefinition> </Grid.ColumnDefinitions> <Label Text="Акселерометр" Grid.Column="0" Grid.Row="0"/> <Label Text="{Binding AccelerometerData}" Grid.Column="1" Grid.Row="0" TextColor="Blue"/> </Grid> </VerticalStackLayout> </ScrollView> </ContentPage>
Все объекты, содержащие данные датчика имеют переопределенный метод ToString()
поэтому мы просто привязываем значение Text
меток Label
к этим объектам. Точно таким же образом мы можем организовать получение данных с других датчиков устройства. Вот как будет выглядеть приложение после запуска акселерометра:
Код модели представления для приложения «Показания датчиков устройства»
Весь код модели представления представлен ниже:
using MauiSensors.Models; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace MauiSensors.ViewModels { public class SensorsViewModel : INotifyPropertyChanged { private List<object> _sensors =[Accelerometer.Default, Compass.Default, Magnetometer.Default, Barometer.Default, Gyroscope.Default, OrientationSensor.Default]; public ObservableCollection<SensorInfo> Sensors { get; set; } = [ new SensorInfo( Accelerometer.Default, false), new SensorInfo( Compass.Default, false), new SensorInfo( Magnetometer.Default, false), new SensorInfo( Barometer.Default, false), new SensorInfo( Gyroscope.Default, false), new SensorInfo( OrientationSensor.Default, false) ]; public ICommand CheckCommand { get; set; } public ICommand StartCommand { get; set; } public AccelerometerData AccelerometerData { get; private set; } public BarometerData BarometerData { get; private set; } public CompassData CompassData { get; private set; } public MagnetometerData MagnetometerData { get; private set; } public GyroscopeData GyroscopeData { get; private set; } public OrientationSensorData OrientationSensorData { get; private set; } public SensorsViewModel() { StartCommand = new Command<SensorInfo>((sensorInfo) => { if (sensorInfo.CanStart) { if (sensorInfo.IsStarted == false) { SubscribeToEvent(sensorInfo.Sensor); var method = sensorInfo.Sensor.GetType().GetMethod(name: "Start", genericParameterCount: 0, [typeof(SensorSpeed)]); var result = method?.Invoke(sensorInfo.Sensor, [SensorSpeed.Default]); } else { UnsubscribeFromEvent(sensorInfo.Sensor); var method = sensorInfo.Sensor.GetType().GetMethod(name: "Stop"); var result = method?.Invoke(sensorInfo.Sensor, null); } } sensorInfo.IsStarted = (bool)sensorInfo.Sensor.GetType().GetProperty("IsMonitoring").GetValue(sensorInfo.Sensor); }); CheckCommand = new Command(() => { Sensors.Clear(); foreach (var sensor in _sensors) { Sensors.Add(new SensorInfo(sensor, (bool)(sensor.GetType().GetProperty("IsSupported").GetValue(sensor)))); } }); } private void Accelerometer_ReadingChanged(object? sender, AccelerometerChangedEventArgs e) { if (AccelerometerData != e.Reading) { AccelerometerData = e.Reading; OnPropertyChanged("AccelerometerData"); } } private void Barometer_ReadingChanged(object? sender, BarometerChangedEventArgs e) { if (BarometerData != e.Reading) { BarometerData = e.Reading; OnPropertyChanged("BarometerData"); } } private void Compass_ReadingChanged(object? sender, CompassChangedEventArgs e) { if (CompassData != e.Reading) { CompassData = e.Reading; OnPropertyChanged("CompassData"); } } private void Magnetometer_ReadingChanged(object? sender, MagnetometerChangedEventArgs e) { if (MagnetometerData != e.Reading) { MagnetometerData = e.Reading; OnPropertyChanged("MagnetometerData"); } } private void Gyroscope_ReadingChanged(object? sender, GyroscopeChangedEventArgs e) { if (GyroscopeData != e.Reading) { GyroscopeData = e.Reading; OnPropertyChanged("GyroscopeData"); } } private void Orientation_ReadingChanged(object? sender, OrientationSensorChangedEventArgs e) { if (OrientationSensorData != e.Reading) { OrientationSensorData = e.Reading; OnPropertyChanged("OrientationSensorData"); } } private void SubscribeToEvent(object sensor) { switch (sensor) { case IAccelerometer: { (sensor as IAccelerometer).ReadingChanged += Accelerometer_ReadingChanged; break; } case IBarometer: { (sensor as IBarometer).ReadingChanged += Barometer_ReadingChanged; break; } case ICompass: { (sensor as ICompass).ReadingChanged += Compass_ReadingChanged; break; } case IMagnetometer: { (sensor as IMagnetometer).ReadingChanged += Magnetometer_ReadingChanged; break; } case IGyroscope: { (sensor as IGyroscope).ReadingChanged += Gyroscope_ReadingChanged; break; } case IOrientationSensor: { (sensor as IOrientationSensor).ReadingChanged += Orientation_ReadingChanged; break; } } } private void UnsubscribeFromEvent(object sensor) { switch (sensor) { case IAccelerometer: { (sensor as IAccelerometer).ReadingChanged -= Accelerometer_ReadingChanged; break; } case IBarometer: { (sensor as IBarometer).ReadingChanged -= Barometer_ReadingChanged; break; } case ICompass: { (sensor as ICompass).ReadingChanged -= Compass_ReadingChanged; break; } case IMagnetometer: { (sensor as IMagnetometer).ReadingChanged -= Magnetometer_ReadingChanged; break; } case IGyroscope: { (sensor as IGyroscope).ReadingChanged -= Gyroscope_ReadingChanged; break; } case IOrientationSensor: { (sensor as IOrientationSensor).ReadingChanged -= Orientation_ReadingChanged; break; } } } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } } }
Итого
Чтобы получать данные с датчиков устройства, необходимо подписаться на событие ReadingChanged
. Каждый тип датчика возвращает свои данные, поэтому нам необходимо знать точный типа датчика, чтобы подписаться на его событие и получать данные.