如何用Kotlin编写Android函数:经纬度转手机指向目标的方位角
实现步骤与代码
要实现手机指向目标位置的方位角计算,核心需要三个数据:手机当前位置、目标位置(传入的经纬度)、手机当前朝向(真北方位角),最终通过「目标相对于当前位置的初始方位角」与「手机当前朝向」的差值,得到0-359范围的结果。
1. 权限配置
在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2. 修正函数参数类型
原函数中经纬度使用Long类型是错误的,经纬度为带小数的数值,必须改为Double类型。
3. 计算两点间的初始方位角
实现工具函数,通过经纬度计算从当前位置到目标位置的初始方位角(以真北为0度,顺时针递增):
fun calculateInitialBearing(currentLat: Double, currentLng: Double, targetLat: Double, targetLng: Double): Double { val φ1 = Math.toRadians(currentLat) val φ2 = Math.toRadians(targetLat) val Δλ = Math.toRadians(targetLng - currentLng) val y = Math.sin(Δλ) * Math.cos(φ2) val x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ) val θ = Math.atan2(y, x) // 将弧度转换为角度,并调整到0-360范围 return (Math.toDegrees(θ) + 360) % 360 }
4. 获取手机当前位置(Fused Location Provider)
用Google Play服务的Fused Location Provider异步获取当前位置,这里用协程实现:
import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException class LocationHelper(private val fusedLocationClient: FusedLocationProviderClient) { suspend fun getCurrentLocation(): android.location.Location? = suspendCancellableCoroutine { continuation -> fusedLocationClient.lastLocation.addOnSuccessListener { location -> continuation.resume(location) }.addOnFailureListener { exception -> continuation.resumeWithException(exception) } } }
5. 获取手机当前朝向(方位角)
通过旋转向量传感器获取手机的真北方位角,用Flow实时监听:
import android.content.Context import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow class OrientationHelper(private val context: Context) { private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager private val rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) fun getAzimuthFlow(): Flow<Float> = callbackFlow { val listener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { val rotationMatrix = FloatArray(9) SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) // 转换为方位角、俯仰角、滚转角 val orientation = FloatArray(3) SensorManager.getOrientation(rotationMatrix, orientation) // 将方位角从-180~180范围转换为0~360 val azimuth = (Math.toDegrees(orientation[0].toDouble()) + 360) % 360 trySend(azimuth.toFloat()) } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } sensorManager.registerListener(listener, rotationVectorSensor, SensorManager.SENSOR_DELAY_NORMAL) awaitClose { sensorManager.unregisterListener(listener) } } }
6. 整合实现目标函数
由于获取位置和传感器数据都是异步操作,采用协程挂起函数实现:
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.coroutines.flow.first // 替换appContext为你的应用Context private val appContext = /* 你的Context实例 */ suspend fun convertLatLongToBearing(targetLat: Double, targetLng: Double): Int = withContext(Dispatchers.IO) { val fusedLocationClient = LocationServices.getFusedLocationProviderClient(appContext) val locationHelper = LocationHelper(fusedLocationClient) val orientationHelper = OrientationHelper(appContext) // 获取当前位置,失败返回-1 val currentLocation = locationHelper.getCurrentLocation() ?: return@withContext -1 // 获取最新的手机方位角 val currentAzimuth = orientationHelper.getAzimuthFlow().first() // 计算目标相对于当前位置的初始方位角 val targetBearing = calculateInitialBearing( currentLocation.latitude, currentLocation.longitude, targetLat, targetLng ) // 计算手机指向目标的最终方位角,调整到0-359范围 val finalBearing = (targetBearing - currentAzimuth + 360) % 360 finalBearing.toInt() }
关键说明
- 调用函数时需在协程作用域内执行(如
lifecycleScope.launch) - 调用前必须确保已获取位置权限,否则无法获取当前位置
- 返回值0代表当前手机已指向目标,数值为顺时针转动的角度
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

