分析
显然,这段代码跟踪手机y轴的方向,在直立的手机上,y轴变得垂直,当手机向你倾斜时,它的地面方向是朝向你的。
是的,这是正确的。你可以检查
getOrientation()
看看发生了什么:
public static float[] getOrientation(float[] R, float[] values) {
/*
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
*/
values[0] = (float) Math.atan2(R[1], R[4]);
...
values[0]
是你得到的方位角值。
你可以解释旋转矩阵
R
作为指向设备三个主轴的矢量分量:
-
第0列:指向电话的矢量
正确的
-
第1列:指向手机的矢量
向上的
-
第2列:指向手机的矢量
前面
矢量是从地球坐标系的角度描述的(
东
,
北
和
天空
)
考虑到这一点,我们可以在
获取方向()
:
-
选择手机的
向上的
轴(矩阵列1,存储在数组元素1、4、7中)
-
把它投射到地球的水平面上(这很容易,忽略
天空
存储在元素7中的组件)
-
使用
atan2
从其余的角度推断
东
和
北
向量的分量。
这里隐藏着另一个微妙之处:
ATAN2
是
public static double atan2(double y, double x);
注意参数顺序:
y
然后
x
. 但是
getOrientation
在中传递参数
东
,
北
秩序。这实现了两件事:
-
使
北
参考轴(在几何学中是
X
轴)
-
反射角度:几何角度是逆时针的,但方位角必须是从北开始的顺时针角度
当然,当电话
向上的
轴是垂直的(“向天空”)然后超过,它的方位角翻转180度。我们可以用一种非常简单的方法解决这个问题:我们将使用
正确的
取而代之的是轴心。注意以下几点:
-
当手机水平朝北时,
正确的
轴与
东
轴。这个
东
在地球坐标系中,轴是“x”几何轴,所以我们的0角参考是正确的。
-
当手机右转(向东)时,它的方位角应该上升,但它的几何角度变为负值。因此我们必须翻转几何角的符号。
解决方案
所以我们的新公式是:
val azimuth = -atan2(R[3], R[0])
而这些琐碎的改变就是你所需要的!不用打电话
GetOrientation公司
,只需将其应用于方向矩阵。
改进的解决方案
到目前为止,还不错。但如果用户在横向使用手机呢?手机的轴不受影响,但现在用户将手机的“左”或“右”方向视为“前方”(取决于用户转动手机的方式)。我们可以通过检查
Display.rotation
财产。如果屏幕旋转,我们将使用
向上的
手机轴的作用与
正确的
轴以上。
因此,定向侦听器的完整代码如下:
private class OrientationListener(
private val activity: Activity,
private val azimuthChanged: (Float) -> Unit,
private val accuracyChanged: (Int) -> Unit
) : SensorEventListener {
private val rotationMatrix = FloatArray(9)
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type != Sensor.TYPE_ROTATION_VECTOR) return
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
val (matrixColumn, sense) = when (val rotation =
activity.windowManager.defaultDisplay.rotation
) {
Surface.ROTATION_0 -> Pair(0, 1)
Surface.ROTATION_90 -> Pair(1, -1)
Surface.ROTATION_180 -> Pair(0, -1)
Surface.ROTATION_270 -> Pair(1, 1)
else -> error("Invalid screen rotation value: $rotation")
}
val x = sense * rotationMatrix[matrixColumn]
val y = sense * rotationMatrix[matrixColumn + 3]
azimuthChanged(-atan2(y, x))
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
if (sensor.type == Sensor.TYPE_ROTATION_VECTOR) {
accuracyChanged(accuracy)
}
}
}
使用这段代码,您得到的行为与google地图上的完全相同。