Для приложения, которое я делаю, мне нужны камера и компас. В манифесте приложение настроено на ландшафтный режим.
Сначала я реализовал компас. Как было предложено разработчиками Android, я использовал два датчика — Акселерометр и Магнитное поле. Вот как я это сделал:
Моя активность реализована SensorEventListener
. В onCreate()
я инициализирую свой sensorManager
, используя:
sManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Я регистрирую своих слушателей в onResume()
вот так:
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),SensorManager.SENSOR_DELAY_NORMAL);
и, конечно же, отменить их регистрацию в onPause()
. Я не использую onAccuracyChanged()
. вот что я делаю в onSensorChanged()
:
@Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_MAGNETIC_FIELD:
mags = event.values.clone();
break;
case Sensor.TYPE_ACCELEROMETER:
accels = event.values.clone();
break;
}
if (mags != null && accels != null) {
gravity = new float[9];
magnetic = new float[9];
SensorManager.getRotationMatrix(gravity, magnetic, accels, mags);
float[] outGravity = new float[9];
float inclination = (float) Math.acos(gravity[8]);
if (inclination < Math.toRadians(25)
|| inclination > Math.toRadians(155)) {
// device is close to flat. Remap for landscape.
SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_Y,SensorManager.AXIS_MINUS_X, outGravity);
SensorManager.getOrientation(outGravity, values);
} else {
// device is not flat. Remap for landscape and perpendicular
SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_X,SensorManager.AXIS_Z, outGravity);
SensorManager.getOrientation(outGravity, values);
}
azimuth = Math.round(Math.toDegrees(values[0]));
}
}
Как видите, я различаю, когда телефон лежит на столе, и когда пользователь держит его (как если бы вы делали снимок). Когда я использую только этот код, все более или менее работает отлично. Я получаю правильные значения азимута как когда телефон лежит на столе, так и когда держу его перпендикулярно столу (разница примерно в 5-10 градусов, но я могу с этим смириться).
Проблема начинается при добавлении предварительный просмотр камеры в приложение. У меня есть реализация моей деятельности SurfaceHolder.Callback
. Я инициализирую свою камеру в onCreate()
:
SurfaceView cameraView = (SurfaceView)findViewById(R.id.camera_view);
surfaceHolder = cameraView.getHolder();
surfaceHolder.addCallback(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
Вот как я реализую интерфейс:
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
camera = Camera.open();
camera.setDisplayOrientation(0);
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
if (isCameraOn) {
camera.stopPreview();
isCameraOn = false;
}
if (camera != null) {
try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
isCameraOn = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
camera.stopPreview();
camera.release();
camera = null;
}
Когда я добавляю код камеры в свой проект и показываю камеру на экране телефона, мои датчики не работают должным образом, когда телефон внезапно оказывается перпендикулярным. Если телефон лежит ровно на столе, значения азимута, которые я получаю, верны. Когда телефон держится перпендикулярно столу, мои значения азимута отклоняются примерно на 40 градусов (хотя и остаются стабильными).
Я пытался найти решение (как самостоятельно, так и в Интернете), но до сих пор усилия были напрасны. Я хотел бы получить указания о том, как решить эту проблему.
Спасибо!