Android照片GPS坐标问题:如何在图片标题栏显示经纬度?
嘿,我来帮你搞定这个GPS坐标显示的问题!你提到已经尝试用Exif但没成功,大概率是在Exif标签的写入格式或者权限环节出了问题,咱们一步步来解决:
1. 先搞定必要的权限
要获取GPS并写入图片元数据,你的App必须先拿到对应的权限,不然系统会直接拒绝操作:
- 在
AndroidManifest.xml里添加权限声明:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- 别忘了在Android 6及以上版本中,动态申请位置和存储权限,不然权限申请不会生效。
2. 正确写入GPS坐标到Exif元数据
Exif的GPS标签有固定的格式要求,不能直接把十进制经纬度写进去,得转换成度分秒格式。给你一个现成的工具类:
public class ExifUtils { public static void writeGPSData(String imagePath, double latitude, double longitude) throws IOException { ExifInterface exif = new ExifInterface(imagePath); // 处理纬度:转换为Exif要求的度分秒格式 String latRef = latitude > 0 ? "N" : "S"; double[] latParts = convertDecimalToDegrees(Math.abs(latitude)); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latParts[0] + "/1," + latParts[1] + "/1," + latParts[2] + "/1000"); exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, latRef); // 处理经度:转换为Exif要求的度分秒格式 String lonRef = longitude > 0 ? "E" : "W"; double[] lonParts = convertDecimalToDegrees(Math.abs(longitude)); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, lonParts[0] + "/1," + lonParts[1] + "/1," + lonParts[2] + "/1000"); exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, lonRef); // 保存修改后的Exif数据 exif.saveAttributes(); } // 把十进制经纬度转成度分秒格式 private static double[] convertDecimalToDegrees(double decimal) { int degrees = (int) decimal; double remainder = decimal - degrees; int minutes = (int) (remainder * 60); double seconds = (remainder * 3600) - (minutes * 60); return new double[]{degrees, minutes, seconds * 1000}; } }
3. 获取当前GPS位置
在拍照完成后,调用位置服务获取当前经纬度,再写入图片的Exif:
private FusedLocationProviderClient fusedLocationClient; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); imageView = findViewById(R.id.imageView); } // 获取当前位置并写入图片Exif private void getCurrentLocationAndWriteExif(String imagePath) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 动态申请位置权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1001); return; } fusedLocationClient.getLastLocation() .addOnSuccessListener(location -> { if (location != null) { double lat = location.getLatitude(); double lon = location.getLongitude(); try { ExifUtils.writeGPSData(imagePath, lat, lon); // 刷新相册,让系统识别到Exif的变化 MediaScannerConnection.scanFile(MainActivity.this, new String[]{imagePath}, null, null); // 读取Exif并显示在标题栏 showGPSCoordinates(imagePath); } catch (IOException e) { e.printStackTrace(); } } }); }
4. 在标题栏显示GPS坐标
有些相册App不会自动显示Exif里的GPS,你可以自己读取Exif并展示在App的标题栏:
private void showGPSCoordinates(String imagePath) { try { String gpsInfo = ExifUtils.getGPSCoordinates(imagePath); // 设置到标题栏 setTitle(gpsInfo); } catch (IOException e) { e.printStackTrace(); setTitle("无GPS坐标"); } } // 给ExifUtils添加读取方法 public static String getGPSCoordinates(String imagePath) throws IOException { ExifInterface exif = new ExifInterface(imagePath); String latRef = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF); String latStr = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE); String lonRef = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF); String lonStr = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE); if (latStr == null || lonStr == null) { return "无GPS坐标"; } // 把度分秒转成十进制格式展示 double latitude = convertDegreesToDecimal(latStr, latRef); double longitude = convertDegreesToDecimal(lonStr, lonRef); return String.format("纬度:%.6f,经度:%.6f", latitude, longitude); } private static double convertDegreesToDecimal(String exifCoord, String ref) { String[] parts = exifCoord.split(","); double degrees = Double.parseDouble(parts[0].split("/")[0]) / Double.parseDouble(parts[0].split("/")[1]); double minutes = Double.parseDouble(parts[1].split("/")[0]) / Double.parseDouble(parts[1].split("/")[1]); double seconds = Double.parseDouble(parts[2].split("/")[0]) / Double.parseDouble(parts[2].split("/")[1]); double decimal = degrees + (minutes / 60) + (seconds / 3600); return ref.equals("S") || ref.equals("W") ? -decimal : decimal; }
几个关键注意点
- 如果你是通过
Bitmap保存图片,一定要手动写入Exif,因为Bitmap本身不会携带任何元数据。 - Android 10及以上版本用
MediaStore保存图片时,建议用ExifInterface的InputStream/OutputStream构造方法来写入,避免路径问题。 - 测试时尽量用真实设备,模拟器的GPS模拟可能不稳定。
内容的提问来源于stack exchange,提问作者Thanos Infosec




