Android 平台提供兩種感應器,可讓您判斷裝置的位置:地磁場感應器和加速計。Android 平台也提供感應器,可讓您判斷 裝置對應至物件 (稱為「鄰近感應器」)。 地磁場感應器與鄰近感應器採用硬體。大多數手機和平板電腦製造商都會加入地磁場感應器。同樣地,手機製造商通常會加入鄰近感應器,用於判斷手機何時靠近使用者的臉部 (例如通話期間)。若要判斷裝置的螢幕方向,可以使用以下來源的讀數: 裝置加速計和地磁場感應器。
注意:方向感應器已在 Android 2.2 版中淘汰 (API 級別 8) 和方向感應器類型已於 Android 4.4W 淘汰 (API 級別 20)。
位置感應器可用於判斷裝置在世界參考架構中的實際位置。例如,您可以使用「Geomagnetic」欄位 感應器,結合加速計來判斷裝置的位置 以及磁鐵的相對基準您也可以使用這些感應器,在應用程式的參考架構中判斷裝置的方向。位置感應器通常不會用於監控裝置的移動或動作,例如搖晃、傾斜或推擠 (詳情請參閱「動作感應器」)。
地磁場感應器和加速度計會針對每個 SensorEvent
傳回感應器值的多維陣列。舉例來說,地磁場感應器會在單一感應器事件期間,為三個座標軸提供地磁場強度值。同樣地,加速計感應器會在感應器事件期間測量裝置的加速度。如要進一步瞭解感應器使用的座標系統,請參閱「感應器座標系統」。接近感應器會為每個感應器事件提供單一值。表 1 列出 Android 平台支援的位置感應器。
表 1. Android 平台支援的位置感應器。
感應器 | 感應器事件資料 | 說明 | 測量單位 |
---|---|---|---|
TYPE_GAME_ROTATION_VECTOR |
SensorEvent.values[0] |
沿著 x 軸的旋轉向量元件 (x * sin(θ/2))。 | 無單位 |
SensorEvent.values[1] |
沿著 Y 軸的旋轉向量元件 (y * sin(while/2))。 | ||
SensorEvent.values[2] |
沿著 z 軸的旋轉向量元件 (z * sin(θ/2))。 | ||
TYPE_GEOMAGNETIC_ROTATION_VECTOR |
SensorEvent.values[0] |
沿著 x 軸的旋轉向量元件 (x * sin(θ/2))。 | 無單位 |
SensorEvent.values[1] |
沿著 Y 軸的旋轉向量元件 (y * sin(while/2))。 | ||
SensorEvent.values[2] |
沿著 Z 軸的旋轉向量元件 (z * sin(while/2))。 | ||
TYPE_MAGNETIC_FIELD |
SensorEvent.values[0] |
沿著 X 軸的地磁場強度。 | μT |
SensorEvent.values[1] |
Y 軸的地磁場強度。 | ||
SensorEvent.values[2] |
沿著 Z 軸的地磁場強度。 | ||
TYPE_MAGNETIC_FIELD_UNCALIBRATED |
SensorEvent.values[0] |
沿著 x 軸的地球磁場強度 (未經過硬鐵校準)。 | μT |
SensorEvent.values[1] |
沿著 y 軸的地球磁場強度 (未經過硬鐵校準)。 | ||
SensorEvent.values[2] |
沿著 z 軸的地球磁場強度 (不含硬鐵校準)。 | ||
SensorEvent.values[3] |
X 軸上的鐵偏差估計值。 | ||
SensorEvent.values[4] |
Y 軸上的鐵偏差估計值。 | ||
SensorEvent.values[5] |
沿著 Z 軸推測出的鐵偏誤。 | ||
TYPE_ORIENTATION 1 個 |
SensorEvent.values[0] |
方位角 (Z 軸的角度)。 | 度 |
SensorEvent.values[1] |
俯仰角 (X 軸周圍的角度)。 | ||
SensorEvent.values[2] |
旋轉 (Y 軸周圍的角度)。 | ||
TYPE_PROXIMITY |
SensorEvent.values[0] |
與物體的距離2。 | 公分 |
1這個感應器已在 Android 2.2 (API) 中淘汰 第 8 級),而且這個感應器類型已在 Android 4.4W (API 級別 20) 中淘汰。 感應器架構提供其他方法,可用於取得裝置方向,詳情請參閱「計算裝置的螢幕方向」一文。
2 部分鄰近感應器只提供代表近和遠的二進位值。
使用遊戲旋轉向量感應器
遊戲旋轉向量感應器與 旋轉 向量感應器,但不會使用地磁場。因此,Y 軸並不會指向北方,而是指向其他參考點。參照的參照可以 與陀螺儀環繞 Z 軸的規模大小相同。
因為遊戲旋轉向量感應器不會使用磁場,所以相對旋轉 ,不受磁場變更的影響。如果您不關心北方的位置,且由於依賴磁場,因此常規旋轉向量不符合您的需求,請在遊戲中使用這個感應器。
以下程式碼說明如何取得預設遊戲旋轉向量感應器的例項:
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
使用地磁旋轉向量感應器
地質旋轉向量感應器與 旋轉向量感應器 而且不會使用陀螺儀。這個感應器的準確度低於一般旋轉向量感應器,但耗電量較低。請僅在您想在背景收集旋轉資訊,且不想耗用過多電池時使用此感測器。這個感應器在使用時機最為實用 搭配批次處理
以下程式碼顯示如何取得預設地磁旋轉值的執行個體 向量感應器:
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);
計算裝置螢幕方向
您可以計算裝置的方向,監控裝置相對於地球參考架構 (具體來說是磁北極) 的位置。下列程式碼說明如何計算裝置的方向:
Kotlin
private lateinit var sensorManager: SensorManager ... // Rotation matrix based on current readings from accelerometer and magnetometer. val rotationMatrix = FloatArray(9) SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading) // Express the updated rotation matrix as three orientation angles. val orientationAngles = FloatArray(3) SensorManager.getOrientation(rotationMatrix, orientationAngles)
Java
private SensorManager sensorManager; ... // Rotation matrix based on current readings from accelerometer and magnetometer. final float[] rotationMatrix = new float[9]; SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // Express the updated rotation matrix as three orientation angles. final float[] orientationAngles = new float[3]; SensorManager.getOrientation(rotationMatrix, orientationAngles);
系統會使用裝置的地磁場感應器和加速計,計算方向角。系統會使用這兩個硬體感應器,提供下列三個方向角度的資料:
- 方位角 (-z 軸的旋轉角度)。這是裝置目前指南針方向與磁北之間的角度。如果裝置的上緣面向磁性,,方位角度為 0 度;如果頂部邊緣朝南,方位角度為 180 度。同樣地 如果上方邊緣面對東方,方位角度為 90 度,如果頂端邊緣為東面 面向西部的面向,方位角度為 270 度。
- 俯仰 (沿 X 軸旋轉的角度)。這是平行於裝置螢幕和平行於地面的平面之間的角度。如果將裝置平行地拿在手上,裝置底部邊緣靠近您,然後將裝置頂部邊緣傾斜至地面,則傾角會變成正值。反方向傾斜 (將裝置頂端遠離地面) 會導致俯仰角變成負值。將介於 90 度。
- 旋轉 (Y 軸旋轉角度)。這是垂直於裝置螢幕的平面,與垂直於地面的平面之間的角度。如果將裝置平行於地面,且底部邊緣最靠近您,並將裝置左側邊緣傾斜至地面,則翻轉角會變成正值。對面則傾斜 方向—將裝置的右側邊緣朝向地面 — 會導致擲骰角度變為負數。值的範圍是 -180 度 溫度設為 180 度
注意:感應器的角色定義已變更,以反映地理感應器生態系統中絕大多數的實作方式。
請注意,這些角度的運作方式,使用的座標系統不同於 一個用於飛行高度 (偏轉、俯仰和旋轉)。在航空系統中,X 軸是沿著飛機的長邊,從機尾到機頭。
方向感應器會透過處理加速計和地磁場感應器的原始感應器資料,擷取資料。由於涉及大量處理作業,因此方向感應器的準確度和精確度會降低。具體來說,這項感應器只有在翻轉角度為 0 時才可靠。因此,Android 已淘汰方向感應器
2.2 (API 級別 8),且 Android 中的方向感應器類型已淘汰
4.4W (API 級別 20)。
建議您不要使用方向感應器的原始資料,
使用getRotationMatrix()
方法搭配
getOrientation()
種方式
計算方向值,如下列程式碼範例所示。裝置
您可以使用
remapCoordinateSystem()
方法,將螢幕方向值轉譯為
參照。
Kotlin
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private val accelerometerReading = FloatArray(3) private val magnetometerReading = FloatArray(3) private val rotationMatrix = FloatArray(9) private val orientationAngles = FloatArray(3) public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } override fun onResume() { super.onResume() // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer -> sensorManager.registerListener( this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField -> sensorManager.registerListener( this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI ) } } override fun onPause() { super.onPause() // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this) } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. override fun onSensorChanged(event: SensorEvent) { if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size) } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size) } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. fun updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix( rotationMatrix, null, accelerometerReading, magnetometerReading ) // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles) // "orientationAngles" now has up-to-date information. } }
Java
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private final float[] accelerometerReading = new float[3]; private final float[] magnetometerReading = new float[3]; private final float[] rotationMatrix = new float[9]; private final float[] orientationAngles = new float[3]; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. // You must implement this callback in your code. } @Override protected void onResume() { super.onResume(); // Get updates from the accelerometer and magnetometer at a constant rate. // To make batch operations more efficient and reduce power consumption, // provide support for delaying updates to the application. // // In this example, the sensor reporting delay is small enough such that // the application receives an update before the system checks the sensor // readings again. Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (accelerometer != null) { sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); if (magneticField != null) { sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } } @Override protected void onPause() { super.onPause(); // Don't receive any more updates from either sensor. sensorManager.unregisterListener(this); } // Get readings from accelerometer and magnetometer. To simplify calculations, // consider storing these readings as unit vectors. @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.length); } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.length); } } // Compute the three orientation angles based on the most recent readings from // the device's accelerometer and magnetometer. public void updateOrientationAngles() { // Update rotation matrix, which is needed to update orientation angles. SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading); // "rotationMatrix" now has up-to-date information. SensorManager.getOrientation(rotationMatrix, orientationAngles); // "orientationAngles" now has up-to-date information. } }
除了將感應器的座標系統轉換為應用程式的參考架構,您通常不需要對裝置的原始方向角執行任何資料處理或篩選作業。
使用地磁場感應器
您可以利用地磁場感應器,監測地球磁場的變化。下列程式碼說明如何取得預設地磁場感應器的例項:
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
注意: 如果應用程式指定 Android 12 (API 級別 31) 或 也就是 頻率限制。
這個感應器會為三個座標軸的每個軸提供原始場強資料 (以微特斯拉為單位)。通常,你不需要直接使用這個感應器。您可以改用旋轉向量感應器來判斷原始旋轉動作,也可以使用加速計和地磁感應器搭配 getRotationMatrix()
方法,取得旋轉矩陣和傾斜矩陣。接著,您可以使用這些矩陣搭配 getOrientation()
和 getInclination()
方法,取得方位角和地磁傾角資料。
注意: 測試應用程式時,可改善的空間 以 8 字形揮動裝置,展現感應器的準確度。
使用未校正的磁力儀
未校正的磁力儀與地磁場感應器類似,但磁場不會套用硬鐵校正。磁場仍會套用工廠校正和溫度補償。未校正的磁力計可用於處理不準確的硬鐵估計值。一般而言,geomagneticsensor_event.values[0]
會接近 uncalibrated_magnetometer_event.values[0] -
uncalibrated_magnetometer_event.values[3]
。也就是說
calibrated_x ~= uncalibrated_x - bias_estimate_x
注意:未校正的傳感器會提供較多原始結果,且可能包含某些偏差,但其測量值中包含的跳躍次數較少,因為校正會套用修正值。有些應用程式可能偏好這些未校正的結果,讓結果更順暢 可靠又可靠的方式舉例來說,如果應用程式嘗試執行自己的感應器融合,導入校正值可能會導致結果失真。
除了磁場之外,未校正的磁力計也會提供各軸的預估硬鐵偏差。以下程式碼說明如何取得預設未校正磁力計的例項:
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);
使用鄰近感應器
鄰近感應器會判斷物體與裝置之間的距離。下列程式碼說明如何取得預設接近感應器的例項:
Kotlin
private lateinit var sensorManager: SensorManager private var sensor: Sensor? = null ... sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
Java
private SensorManager sensorManager; private Sensor sensor; ... sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
鄰近感應器通常用於判斷人頭與臉部之間的距離 的手機裝置上 (例如撥打或接聽電話時)。大多數 鄰近感應器會傳回絕對距離 (公分),但部分感應器只會在 。
注意:在某些裝置型號中,鄰近感應器位於螢幕下方,如果在螢幕開啟時啟用鄰近感應器,螢幕上可能會出現閃爍的圓點。
以下程式碼會說明如何使用接近感應器:
Kotlin
class SensorActivity : Activity(), SensorEventListener { private lateinit var sensorManager: SensorManager private var proximity: Sensor? = null public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) // Get an instance of the sensor service, and use that to get an instance of // a particular sensor. sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do something here if sensor accuracy changes. } override fun onSensorChanged(event: SensorEvent) { val distance = event.values[0] // Do something with this sensor data. } override fun onResume() { // Register a listener for the sensor. super.onResume() proximity?.also { proximity -> sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL) } } override fun onPause() { // Be sure to unregister the sensor when the activity pauses. super.onPause() sensorManager.unregisterListener(this) } }
Java
public class SensorActivity extends Activity implements SensorEventListener { private SensorManager sensorManager; private Sensor proximity; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Get an instance of the sensor service, and use that to get an instance of // a particular sensor. sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } @Override public final void onSensorChanged(SensorEvent event) { float distance = event.values[0]; // Do something with this sensor data. } @Override protected void onResume() { // Register a listener for the sensor. super.onResume(); sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onPause() { // Be sure to unregister the sensor when the activity pauses. super.onPause(); sensorManager.unregisterListener(this); } }
注意:部分接近感應器會傳回代表「近」或「遠」的二進位值。在這種情況下,感應器通常會回報最遠狀態的最大範圍值
而接近狀態時的值也較小一般而言,最遠值是指 >5 公分,但實際時間可能會有所差異
包括感應器和感應器您可以使用 getMaximumRange()
方法判斷感應器的最大範圍。