Разработка под Android / Собираем показания датчиков с Android смартфона. Работа над ошибками

в 5:56, , рубрики: android, sensor, датчики, метки: , ,

В своем предыдущем посте я рассказал, как получить углы наклона аппарата во всех трех плоскостях. Однако, как оказалось, метод, использованный в топике является deprecated начиная с API Level 8 (Android 2.2). Исправлю эту ошибку и расскажу, как правильно получать данные под катом.
Сначала чуть-чуть теории

В документации по Android нам предлагают вместо SENSOR_ORIENTATION использовать метод
getOrientation (float[] R, float[] values)

Этот метод принимает два параметра:R — RotationMatrix или матрица поворота устройства;

values — массив из трех элементов типа float, в который запишутся углы наклона аппарата в радианах;

Значит, для осуществления цели нам всего лишь необходимо получить ту самую матрицу поворота. А получить ее можно из другого метода:
getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic)

Данный метод в первые два массива помещает матрицу поворота и матрицу отклонения аппарата от (магнитного полюса Земли ?). Для этого ей необходимо также передать данные с датчика акселерометра и геомагнитного датчика. К счастью TYPE_ACCELEROMETER и TYPE_MAGNETIC_FIELD не являются deprecated. Следовательно, с них мы и будем снимать показания.
Переходим к практике

Layout мы можем взять тот же, что был в прошлом примере. Вот он:

/>

В главной активити объявим переменные:
private SensorManager msensorManager; //Менеджер сенсоров аппрата

private float[] rotationMatrix; //Матрица поворота
private float[] accelData; //Данные с акселерометра
private float[] magnetData; //Данные геомагнитного датчика
private float[] OrientationData; //Матрица положения в пространстве

public TextView xyView;
public TextView xzView;
public TextView zyView;

Метод onCreate должен реализовывать методы класса SensorEventListener, поэтому его объявление изменится:
public class Main extends Activity implements SensorEventListener{

И добавятся два обязательных метода onAccuracyChanged и onSensorChanged.
А перед setContentView пишем:
msensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

rotationMatrix = new float[16];
accelData = new float[3];
magnetData = new float[3];
OrientationData = new float[3];

xyView = (TextView) findViewById(R.id.xyValue); //
xzView = (TextView) findViewById(R.id.xzValue); // Наши текстовые поля для вывода показаний
zyView = (TextView) findViewById(R.id.zyValue); //

Если читали мой прошлый пост, то ничего нового пока не увидели. Если же нет, то Вам нужно знать, что в первой строчке мы получаем объект менеджера датчиков.
Теперь создадим метод onResume, в котором уточним, данные каких датчиков нам необходимы:
@Override
protected void onResume() {
super.onResume();
msensorManager.registerListener(this, msensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI );
msensorManager.registerListener(this, msensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_UI );
}

Здесь мы передаем в метод registerListener тип нужного нам датчика (полный список обозначений датчиков) и частоту обновления данных (может быть SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, or SENSOR_DELAY_FASTEST в порядке увеличения частоты).
Чтобы наша программа не съедала ресурсы смартфона будучи свернутой по событию onPause «снимаем» получение данных с датчиков:
@Override
protected void onPause() {
super.onPause();
msensorManager.unregisterListener(this);
}

Давайте создадим новый метод, в котором данные с датчиков будем заносить в соответствующий датчику массив. Назовем метод loadNewSensorData:
private void loadNewSensorData(SensorEvent event) {
final int type = event.sensor.getType(); //Определяем тип датчика
if (type == Sensor.TYPE_ACCELEROMETER) { //Если акселерометр
accelData = event.values.clone();
}

if (type == Sensor.TYPE_MAGNETIC_FIELD) { //Если геомагнитный датчик
magnetData = event.values.clone();
}
}

Ну вот, почти и все! Осталось только написать обработчик события onSensorChanged:
public void onSensorChanged(SensorEvent event) {
loadNewSensorData(event); // Получаем данные с датчика
SensorManager.getRotationMatrix(rotationMatrix, null, accelData, magnetData); //Получаем матрицу поворота
SensorManager.getOrientation(rotationMatrix, OrientationData); //Получаем позицию устройства в пространстве

if((xyView==null)||(xzView==null)||(zyView==null)){ //Без этого работать отказалось.
xyView = (TextView) findViewById(R.id.xyValue);
xzView = (TextView) findViewById(R.id.xzValue);
zyView = (TextView) findViewById(R.id.zyValue);
}

//Выводим результат
xyView.setText(String.valueOf(Math.round(Math.toDegrees(OrientationData[0]))));
xzView.setText(String.valueOf(Math.round(Math.toDegrees(OrientationData[1]))));
zyView.setText(String.valueOf(Math.round(Math.toDegrees(OrientationData[2]))));
}

Все готово! Теперь я исправил свою ошибку и научил вас получать данные с датчиков не deprecated способом. :)
В заключении приведу ссылки на исходники, на готовый apk и на источник информации.
P.S.: Вы наверняка заметили лишнюю проверку переменных на отличие от null в последнем листинге. Без нее компилятор выдает java.lang.NullPointerException. Буду признателен, если кто-нибудь объяснит или укажет на ошибку.


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js