You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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保存图片时,建议用ExifInterfaceInputStream/OutputStream构造方法来写入,避免路径问题。
  • 测试时尽量用真实设备,模拟器的GPS模拟可能不稳定。

内容的提问来源于stack exchange,提问作者Thanos Infosec

火山引擎 最新活动