Pull to refresh

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

Reading time5 min
Views26K
В своем предыдущем посте я рассказал, как получить углы наклона аппарата во всех трех плоскостях. Однако, как оказалось, метод, использованный в топике является 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 мы можем взять тот же, что был в прошлом примере. Вот он:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="Угол XY" />

        <TextView
            android:id="@+id/xyValue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="0" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView3"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="Угол XZ" /> />

        <TextView
            android:id="@+id/xzValue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="0" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView5"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="Угол ZY" />

        <TextView
            android:id="@+id/zyValue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="25dp"
            android:text="0" />

    </LinearLayout>

</LinearLayout>


В главной активити объявим переменные:

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

    private TextView xyView;
    private TextView xzView;
    private 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. Буду признателен, если кто-нибудь объяснит или укажет на ошибку.

UPD: Проблема решилась благодаря firexel.
Переставьте местами setContentView и строчки с findViewById в onCreate

После этого можно убрать лишнюю проверку.
Tags:
Hubs:
+14
Comments11

Articles

Change theme settings