0

0

Android Studio中获取用户当前位置并显示在地图上的教程

聖光之護

聖光之護

发布时间:2025-10-15 09:23:16

|

381人浏览过

|

来源于php中文网

原创

Android Studio中获取用户当前位置并显示在地图上的教程

本教程旨在解决android应用中获取用户当前位置并将其显示在google地图上时常遇到的`latlng`为空的问题。核心在于理解位置获取操作的异步性,并正确处理权限请求与地图初始化时机。我们将详细介绍如何配置项目、请求运行时权限、使用`fusedlocationproviderclient`获取位置,并确保在位置数据可用后才更新地图,从而避免程序崩溃并成功显示用户位置。

在Android应用开发中,集成Google地图并显示用户当前位置是一个常见需求。然而,由于位置信息获取是异步操作,开发者常会遇到在位置数据实际可用之前尝试使用它,导致LatLng对象为null,进而引发应用崩溃的问题。本教程将提供一个全面的指南,详细阐述如何在Android Studio中通过Java正确地获取用户当前位置,并在Google地图上进行展示。

1. 项目准备与依赖配置

首先,确保你的Android项目已正确配置Google Play Services和Google Maps SDK。

1.1 build.gradle (app) 添加依赖

在dependencies块中添加以下库:

dependencies {
    // Google Maps SDK
    implementation 'com.google.android.gms:play-services-maps:18.2.0'
    // Location Services
    implementation 'com.google.android.gms:play-services-location:21.0.1'
    // 其他你的依赖
}

1.2 AndroidManifest.xml 配置

标签外部(作为的子标签)添加必要的权限:



标签内部添加Google Maps API Key:

Figma
Figma

Figma 是一款基于云端的 UI 设计工具,可以在线进行产品原型、设计、评审、交付等工作。

下载


     

    
        
            
            
        
    

请确保将YOUR_API_KEY替换为你在Google Cloud Console中获取的实际API Key。

2. 运行时权限请求

从Android 6.0 (API level 23) 开始,危险权限(如位置权限)需要在运行时动态请求。

// activity_hospitals.java
public class activity_hospitals extends FragmentActivity implements OnMapReadyCallback {
    // ... 其他成员变量

    private static final int LOCATION_PERMISSION_REQUEST_CODE = 100;
    private GoogleMap Gmap;
    private FusedLocationProviderClient flsc;
    private LatLng userCurrentLocation; // 用于存储用户当前位置

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hospitals);
        // ... 其他初始化

        flsc = LocationServices.getFusedLocationProviderClient(this);

        // 初始化地图片段,但此时不直接调用getMapAsync,等待位置权限和数据
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        if (mapFragment != null) {
            mapFragment.getMapAsync(this); // 异步获取GoogleMap对象
        }

        // 在onCreate中检查并请求权限
        checkLocationPermission();
    }

    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            // 权限已授予,可以直接获取位置
            getLastLocation();
        } else {
            // 权限未授予,请求权限
            requestLocationPermission();
        }
    }

    private void requestLocationPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予,获取位置
                getLastLocation();
            } else {
                // 权限被拒绝
                Toast.makeText(this, "需要位置权限才能显示您的位置", Toast.LENGTH_SHORT).show();
                // 可以在这里提供一个解释,或禁用依赖位置的功能
            }
        }
    }

    // ... 其他方法
}

3. 获取用户当前位置 (异步处理)

获取位置信息是一个异步操作。flsc.getLastLocation().addOnSuccessListener()方法会在成功获取到位置时回调。因此,任何依赖于userCurrentLocation变量的操作都应该在此回调内部或由其触发。

// activity_hospitals.java
public class activity_hospitals extends FragmentActivity implements OnMapReadyCallback {
    // ... 其他成员变量和方法

    public void getLastLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            flsc.getLastLocation().addOnSuccessListener(this, location -> {
                if (location != null) {
                    // 成功获取到位置
                    userCurrentLocation = new LatLng(location.getLatitude(), location.getLongitude());
                    // 此时位置数据已可用,可以更新地图
                    updateMapWithUserLocation();
                } else {
                    // 无法获取到位置,可能是设备位置服务未开启或无历史位置记录
                    Toast.makeText(this, "无法获取您的当前位置,请检查位置服务设置", Toast.LENGTH_LONG).show();
                    // 可以设置一个默认位置或提示用户手动输入
                    setDefaultLocation();
                }
            }).addOnFailureListener(e -> {
                // 获取位置失败,例如权限问题或服务不可用
                Toast.makeText(this, "获取位置失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
                setDefaultLocation();
            });
        } else {
            // 权限未授予,理论上在checkLocationPermission()中已处理
            Toast.makeText(this, "位置权限被拒绝", Toast.LENGTH_SHORT).show();
            setDefaultLocation();
        }
    }

    private void setDefaultLocation() {
        // 设置一个默认位置,例如地图中心或某个城市的中心
        userCurrentLocation = new LatLng(33.71456158807447, 35.48425016137045); // 示例默认位置
        updateMapWithUserLocation();
    }

    // ... 其他方法
}

关键点: userCurrentLocation只有在addOnSuccessListener回调中才会被赋值。如果在该回调外部立即尝试使用userCurrentLocation,它将是null。

4. 在地图上显示用户位置

onMapReady回调会在GoogleMap对象可用时触发。我们需要确保在onMapReady中,userCurrentLocation已经有值。最健壮的方法是,当userCurrentLocation被成功获取后,再调用一个方法来更新地图。

// activity_hospitals.java
public class activity_hospitals extends FragmentActivity implements OnMapReadyCallback {
    // ... 其他成员变量和方法

    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        Gmap = googleMap;
        // 启用地图上的我的位置层(蓝点和指南针)
        try {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                Gmap.setMyLocationEnabled(true);
            }
        } catch (SecurityException e) {
            Log.e("Map", "My location permission not granted", e);
        }

        // 如果地图已经准备好并且位置也已获取,则直接更新地图
        if (userCurrentLocation != null) {
            updateMapWithUserLocation();
        } else {
            // 如果位置尚未获取,则在位置获取成功时再更新地图
            // 此时可以显示一个加载指示器或默认位置
            // 例如,可以等待getLastLocation()成功后调用updateMapWithUserLocation()
            setDefaultLocation(); // 在位置未获取到时,先显示默认位置
        }

        // 添加其他静态标记
        LatLng placeholder1 = new LatLng(33.66535378588594, 35.420147180348465);
        Gmap.addMarker(new MarkerOptions().position(placeholder1).title("Dr. Monzer al Haj Hospital"));

        LatLng placeholder2 = new LatLng(33.76696201016636, 35.48301133270906);
        Gmap.addMarker(new MarkerOptions().position(placeholder2).title("SSH"));
    }

    /**
     * 当用户位置数据可用时,更新地图显示
     */
    private void updateMapWithUserLocation() {
        if (Gmap != null && userCurrentLocation != null) {
            // 清除旧的标记(如果需要)
            Gmap.clear();

            // 添加用户位置标记
            Gmap.addMarker(new MarkerOptions().position(userCurrentLocation).title("您的位置"));

            // 移动摄像头到用户位置
            float zoomLevel = 15.0f; // 适当的缩放级别
            Gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(userCurrentLocation, zoomLevel));

            // 重新添加其他静态标记
            LatLng placeholder1 = new LatLng(33.66535378588594, 35.420147180348465);
            Gmap.addMarker(new MarkerOptions().position(placeholder1).title("Dr. Monzer al Haj Hospital"));

            LatLng placeholder2 = new LatLng(33.76696201016636, 35.48301133270906);
            Gmap.addMarker(new MarkerOptions().position(placeholder2).title("SSH"));
        }
    }

    // ... 其他方法
}

5. 完整代码示例 (activity_hospitals.java)

package com.example.mobileproject;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;

import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

public class activity_hospitals extends FragmentActivity implements OnMapReadyCallback {
    private Toolbar mytoolbar;
    private GoogleMap Gmap;
    private FusedLocationProviderClient flsc;
    private LatLng userCurrentLocation; // 存储用户当前位置
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hospitals);

        mytoolbar = findViewById(R.id.hospitalToolbar);
        mytoolbar.setTitle("Hospitals Near You");

        // 初始化 FusedLocationProviderClient
        flsc = LocationServices.getFusedLocationProviderClient(this);

        // 获取地图片段并注册回调
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        if (mapFragment != null) {
            mapFragment.getMapAsync(this);
        }

        // 检查并请求位置权限
        checkLocationPermission();
    }

    private void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            // 权限已授予,获取位置
            getLastLocation();
        } else {
            // 权限未授予,请求权限
            requestLocationPermission();
        }
    }

    private void requestLocationPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予,获取位置
                getLastLocation();
            } else {
                // 权限被拒绝
                Toast.makeText(this, "需要位置权限才能显示您的位置", Toast.LENGTH_SHORT).show();
                setDefaultLocation(); // 权限拒绝时设置默认位置
            }
        }
    }

    public void getLastLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            flsc.getLastLocation().addOnSuccessListener(this, location -> {
                if (location != null) {
                    userCurrentLocation = new LatLng(location.getLatitude(), location.getLongitude());
                    updateMapWithUserLocation(); // 位置获取成功后更新地图
                } else {
                    Toast.makeText(this, "无法获取您的当前位置,请检查位置服务设置", Toast.LENGTH_LONG).show();
                    setDefaultLocation(); // 无法获取位置时设置默认位置
                }
            }).addOnFailureListener(e -> {
                Log.e("Location", "Failed to get last location", e);
                Toast.makeText(this, "获取位置失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
                setDefaultLocation(); // 获取位置失败时设置默认位置
            });
        } else {
            // 权限未授予,理论上在checkLocationPermission()中已处理
            setDefaultLocation();
        }
    }

    private void setDefaultLocation() {
        // 设置一个默认位置,例如地图中心或某个城市的中心
        userCurrentLocation = new LatLng(33.71456158807447, 35.48425016137045); // 示例默认位置
        updateMapWithUserLocation();
    }

    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        Gmap = googleMap;
        try {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                Gmap.setMyLocationEnabled(true); // 启用地图上的我的位置层
            }
        } catch (SecurityException e) {
            Log.e("Map", "My location permission not granted", e);
        }

        // 如果地图已经准备好并且位置也已获取,则直接更新地图
        // 否则,等待位置获取成功后,由updateMapWithUserLocation()更新
        if (userCurrentLocation != null) {
            updateMapWithUserLocation();
        } else {
            // 如果userCurrentLocation仍为null,则等待getLastLocation()的回调来触发updateMapWithUserLocation()
            // 或者,可以在这里显示一个加载状态或默认位置
            setDefaultLocation(); // 确保地图初始化时总有一个位置显示
        }
    }

    /**
     * 当用户位置数据可用时,更新地图显示
     */
    private void updateMapWithUserLocation() {
        if (Gmap != null && userCurrentLocation != null) {
            Gmap.clear(); // 清除所有标记,以便重新添加

            // 添加用户位置标记
            Gmap.addMarker(new MarkerOptions().position(userCurrentLocation).title("您的位置"));

            // 移动摄像头到用户位置
            float zoomLevel = 15.0f; // 适当的缩放级别
            Gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(userCurrentLocation, zoomLevel));

            // 重新添加其他静态标记
            LatLng placeholder1 = new LatLng(33.66535378588594, 35.420147180348465);
            Gmap.addMarker(new MarkerOptions().position(placeholder1).title("Dr. Monzer al Haj Hospital"));

            LatLng placeholder2 = new LatLng(33.76696201016636, 35.48301133270906);
            Gmap.addMarker(new MarkerOptions().position(placeholder2).title("SSH"));
        }
    }
}

注意事项与总结

  1. 异步性是关键: 记住位置获取是一个异步过程。getLastLocation()不会立即返回位置,而是通过addOnSuccessListener回调提供结果。因此,所有依赖于位置数据的UI更新或逻辑处理都必须在该回调内部执行或被其触发。
  2. 权限处理: 务必在获取位置之前检查并请求运行时权限。如果权限被拒绝,应提供友好的用户反馈或使用默认位置。
  3. userCurrentLocation的初始化时机: 确保userCurrentLocation在被使用之前已经被赋值。在onMapReady中,如果userCurrentLocation仍为null,说明位置尚未获取到,此时应等待getLastLocation的回调或者显示一个默认位置。
  4. 错误处理: 考虑位置服务未开启、无历史位置记录或获取位置失败等情况,并提供相应的用户提示和备用方案(例如设置默认位置)。
  5. 地理编码(可选): 如果需要将经纬度转换为可读的地址信息,可以使用Geocoder类。但在本教程中,我们主要关注获取经纬度并显示。
  6. 连续位置更新: getLastLocation()仅提供最后已知的位置。如果需要连续或更精确的位置更新,应使用flsc.requestLocationUpdates()方法。

通过遵循以上步骤和注意事项,你可以成功地在Android应用中获取并显示用户的当前位置,同时避免常见的LatLng为null的错误。理解异步编程范式是解决此类问题的核心。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

438

2024.03.01

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1899

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2091

2024.08.01

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

1061

2024.11.28

console接口是干嘛的
console接口是干嘛的

console接口是一种用于在计算机命令行或浏览器开发工具中输出信息的工具,提供了一种简单的方式来记录和查看应用程序的输出结果和调试信息。本专题为大家提供console接口相关的各种文章、以及下载和课程。

415

2023.08.08

console.log是什么
console.log是什么

console.log 是 javascript 函数,用于在浏览器控制台中输出信息,便于调试和故障排除。想了解更多console.log的相关内容,可以阅读本专题下面的文章。

504

2024.05.29

android开发三大框架
android开发三大框架

android开发三大框架是XUtil框架、volley框架、ImageLoader框架。本专题为大家提供android开发三大框架相关的各种文章、以及下载和课程。

285

2023.08.14

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

17

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.8万人学习

Java 教程
Java 教程

共578课时 | 52.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号