我有一个使用旋转矢量传感器的Android应用。阅读评论here后,我发现使用较新的API级别,SensorEvent包含4-5个值,而不是3.我的代码使用arraycopy来获取数据。
lastRotVal是一个类成员,并初始化为size [3]的数组。以下是响应传感器事件的代码的相关部分。
public void onSensorChanged(SensorEvent event) {
System.arraycopy(event.values, 0, lastRotVal, 0, 3); //Hardcoded size
lastRotValSet = true;
updateDisplay(event);
}
private void updateDisplay(SensorEvent event){
if (lastRotValSet){
float[] rotation = new float[9];
float[] orientation = new float[3];
SensorManager.getRotationMatrixFromVector(rotation, lastRotVal);
SensorManager.getOrientation(rotation, orientation);
double pitch = orientation[1];
double roll = orientation[2];
//Do stuff with roll and pitch
}
}
我在arraycopy中只使用了3个值进行了硬编码。它似乎适用于较旧的和新的API级别。这是保持版本之间兼容性的最佳方式,还是我可以做得更好?
编辑:如接受的答案所述,提示此问题的IllegalArgumentException显然是由于三星设备上的API中的错误而导致的,而不是一般的API版本。所以我要补充一个事实,即在三星Galaxy SIII上遇到了我的初始错误。
答案 0 :(得分:3)
The post实际上是指少数三星设备中的错误(Galaxy S4,Galaxy Note 3) - 请参阅this Android Developer list post。实际上,您不必在SDK级别之间进行任何特殊处理,以使此代码可以在普通设备上运行。但是,唉,碎片......
如果大小大于4,则截断数组if (values.length > 4) {
// On some Samsung devices SensorManager.getRotationMatrixFromVector
// appears to throw an exception if rotation vector has length > 4.
// For the purposes of this class the first 4 values of the
// rotation vector are sufficient (see crbug.com/335298 for details).
if (mTruncatedRotationVector == null) {
mTruncatedRotationVector = new float[4];
}
System.arraycopy(values, 0, mTruncatedRotationVector, 0, 4);
getOrientationFromRotationVector(mTruncatedRotationVector);
} else {
getOrientationFromRotationVector(values);
}
但是,我在我的应用GPSTest中发现此解决方案似乎不适用于Galaxy S3(请参阅Github问题here)。
所以,我最终只是在抛出IllegalArgumentException
的设备上截断了数组。除非绝对必要,否则这也避免了额外的System.arraycopy()。
这是代码片段(在API级别低于Gingerbread的设备上也支持方向传感器(即,在引入ROTATION_VECTOR传感器之前),并处理重新映射坐标系以进行方向更改),它使用类成员mTruncateVector
已初始化为false
:
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onSensorChanged(SensorEvent event) {
double orientation = Double.NaN;
double tilt = Double.NaN;
switch (event.sensor.getType()) {
case Sensor.TYPE_ROTATION_VECTOR:
// Modern rotation vector sensors
if (!mTruncateVector) {
try {
SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
} catch (IllegalArgumentException e) {
// On some Samsung devices, an exception is thrown if this vector > 4 (see #39)
// Truncate the array, since we can deal with only the first four values
Log.e(TAG, "Samsung device error? Will truncate vectors - " + e);
mTruncateVector = true;
// Do the truncation here the first time the exception occurs
getRotationMatrixFromTruncatedVector(event.values);
}
} else {
// Truncate the array to avoid the exception on some devices (see #39)
getRotationMatrixFromTruncatedVector(event.values);
}
int rot = getWindowManager().getDefaultDisplay().getRotation();
switch (rot) {
case Surface.ROTATION_0:
// No orientation change, use default coordinate system
SensorManager.getOrientation(mRotationMatrix, mValues);
// Log.d(TAG, "Rotation-0");
break;
case Surface.ROTATION_90:
// Log.d(TAG, "Rotation-90");
SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_Y,
SensorManager.AXIS_MINUS_X, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
case Surface.ROTATION_180:
// Log.d(TAG, "Rotation-180");
SensorManager
.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_X,
SensorManager.AXIS_MINUS_Y, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
case Surface.ROTATION_270:
// Log.d(TAG, "Rotation-270");
SensorManager
.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_Y,
SensorManager.AXIS_X, mRemappedMatrix);
SensorManager.getOrientation(mRemappedMatrix, mValues);
break;
default:
// This shouldn't happen - assume default orientation
SensorManager.getOrientation(mRotationMatrix, mValues);
// Log.d(TAG, "Rotation-Unknown");
break;
}
orientation = Math.toDegrees(mValues[0]); // azimuth
tilt = Math.toDegrees(mValues[1]);
break;
case Sensor.TYPE_ORIENTATION:
// Legacy orientation sensors
orientation = event.values[0];
break;
default:
// A sensor we're not using, so return
return;
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private void getRotationMatrixFromTruncatedVector(float[] vector) {
System.arraycopy(vector, 0, mTruncatedRotationVector, 0, 4);
SensorManager.getRotationMatrixFromVector(mRotationMatrix, mTruncatedRotationVector);
}
并在onResume()
注册传感器:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// Use the modern rotation vector sensors
Sensor vectorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
mSensorManager.registerListener(this, vectorSensor, 16000); // ~60hz
} else {
// Use the legacy orientation sensors
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
if (sensor != null) {
mSensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_GAME);
}
}
完整实施是here on Github。
答案 1 :(得分:1)
简短摘要:假设您不想使用值[3]和值[4],您的代码就可以了。
来自文档:
values[0]: x*sin(θ/2)
values[1]: y*sin(θ/2)
values[2]: z*sin(θ/2)
values[3]: cos(θ/2)
values[4]: estimated heading Accuracy (in radians) (-1 if unavailable)
values[3], originally optional, will always be present from SDK Level 18 onwards. values[4] is a new value that has been added in SDK Level 18.
如果我读得正确,如果使用SDK 18或更早版本编译,则event.values.length只会大于3。
SensorManager.getRotationMatrixFromVector似乎假设一个长度为== 3的旋转矢量。如果传入的旋转矢量大于3个元素,我不确定该函数会起什么作用。
如果你需要使用event.values [3]和event.values [4],你可以通过检查event.values.length来检测设备是否支持这些扩展值。您还可以在运行时检查Build.VERSION.SDK_INT是否为> = 18。但如果你不需要它,坚持你的硬编码假设3。