如何借助Geocoder在onSensorChanged方法中使用用户当前坐标实现指南针
解决方案
核心改动说明
- 新增私有成员变量存储用户当前经纬度和位置有效性标记,在
onLocationChanged回调中实时更新用户位置 - 新增位置服务相关的注册/注销方法,确保可以获取系统推送的位置更新
- 优化
onSensorChanged逻辑,判断位置有效后再代入计算目标方位角,同时删除冗余的重复计算代码 - 预留目标坐标的设置入口,方便后续灵活修改指向的目标位置
修改后完整代码
public class Compass implements SensorEventListener, LocationListener { private static final String TAG = "Compass"; // 新增:存储用户当前位置 private double userLat = 0.0; private double userLng = 0.0; // 新增:标记位置是否有效,避免无位置时计算出错 private boolean isLocationValid = false; // 新增:目标坐标,可通过set方法修改 private double targetLat = 21.422487; private double targetLng = 39.826206; protected double bearing(double startLat, double startLng, double endLat, double endLng){ double longitude1 = startLng; double longitude2 = endLng; double latitude1 = Math.toRadians(startLat); double latitude2 = Math.toRadians(endLat); double longDiff= Math.toRadians(longitude2-longitude1); double y= Math.sin(longDiff)*Math.cos(latitude2); double x=Math.cos(latitude1)*Math.sin(latitude2)-Math.sin(latitude1)*Math.cos(latitude2)*Math.cos(longDiff); return (Math.toDegrees(Math.atan2(y, x))+360)%360; } Geocoder geocoder; List<Address> addresses; @Override public void onSensorChanged(SensorEvent event) { final float alpha = 0.97f; synchronized (this) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0]; mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1]; mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2]; } if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha) * event.values[0]; mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha) * event.values[1]; mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha) * event.values[2]; } boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic); if (success) { float orientation[] = new float[3]; SensorManager.getOrientation(R, orientation); azimuth = (float) Math.toDegrees(orientation[0]); azimuth = (azimuth + azimuthFix + 360) % 360; // 新增:只有位置有效时才计算指向目标的方位差 if (isLocationValid) { double targetBearing = bearing(userLat, userLng, targetLat, targetLng); azimuth = (azimuth - (float)targetBearing + 360) % 360; } if (listener != null) { listener.onNewAzimuth(azimuth); } } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onLocationChanged(@NonNull Location location) { // 新增:更新用户位置 userLat = location.getLatitude(); userLng = location.getLongitude(); isLocationValid = true; } // 新增:设置目标坐标的方法 public void setTargetLocation(double lat, double lng) { this.targetLat = lat; this.targetLng = lng; } // 新增:注册位置监听的方法,需要外部先获取位置权限 public void registerLocationListener(LocationManager locationManager) throws SecurityException { // 可以根据需求调整位置更新的最小时间和距离阈值 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1f, this); // 也可以同时注册网络位置源,提高室内定位可用性 locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 1f, this); } // 新增:注销位置监听的方法 public void unregisterLocationListener(LocationManager locationManager) { locationManager.removeUpdates(this); isLocationValid = false; } public interface CompassListener { void onNewAzimuth(float azimuth); } private CompassListener listener; private SensorManager sensorManager; private Sensor gsensor; private Sensor msensor; private float[] mGravity = new float[3]; private float[] mGeomagnetic = new float[3]; private float[] R = new float[9]; private float[] I = new float[9]; private float azimuth; private float azimuth2; private float azimuthFix; public Compass(Context context) { sensorManager = (SensorManager) context .getSystemService(Context.SENSOR_SERVICE); gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); } public void start() { sensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME); sensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME); } public void stop() { sensorManager.unregisterListener(this); } public void setAzimuthFix(float fix) { azimuthFix = fix; } public void resetAzimuthFix() { setAzimuthFix(0); } public void setListener(CompassListener l) { listener = l; } }
调用步骤
- 先在AndroidManifest.xml中添加所需权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" />
- 在Activity中先动态申请位置权限,权限通过后再执行以下逻辑:
// 初始化 Compass compass = new Compass(this); LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); // 设置回调 compass.setListener(azimuth -> { // 这里更新指南针UI,旋转对应角度即可 yourCompassView.setRotation(-azimuth); }); // 启动传感器和位置监听 compass.start(); try { compass.registerLocationListener(locationManager); } catch (SecurityException e) { e.printStackTrace(); }
- 在Activity销毁时注销监听避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); compass.stop(); compass.unregisterLocationListener(locationManager); }
内容的提问来源于stack exchange,提问作者Abdul Hadi
相关产品推荐
相关产品推荐

