
本教程旨在解决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:
请确保将YOUR_API_KEY替换为你在Google Cloud Console中获取的实际API Key。
2. 运行时权限请求
从Android 6.0 (API level 23) 开始,危险权限(如位置权限)需要在运行时动态请求。
// activity_hospitals.javapublic 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.javapublic 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.javapublic 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")); } }}
注意事项与总结
异步性是关键: 记住位置获取是一个异步过程。getLastLocation()不会立即返回位置,而是通过addOnSuccessListener回调提供结果。因此,所有依赖于位置数据的UI更新或逻辑处理都必须在该回调内部执行或被其触发。权限处理: 务必在获取位置之前检查并请求运行时权限。如果权限被拒绝,应提供友好的用户反馈或使用默认位置。userCurrentLocation的初始化时机: 确保userCurrentLocation在被使用之前已经被赋值。在onMapReady中,如果userCurrentLocation仍为null,说明位置尚未获取到,此时应等待getLastLocation的回调或者显示一个默认位置。错误处理: 考虑位置服务未开启、无历史位置记录或获取位置失败等情况,并提供相应的用户提示和备用方案(例如设置默认位置)。地理编码(可选): 如果需要将经纬度转换为可读的地址信息,可以使用Geocoder类。但在本教程中,我们主要关注获取经纬度并显示。连续位置更新: getLastLocation()仅提供最后已知的位置。如果需要连续或更精确的位置更新,应使用flsc.requestLocationUpdates()方法。
通过遵循以上步骤和注意事项,你可以成功地在Android应用中获取并显示用户的当前位置,同时避免常见的LatLng为null的错误。理解异步编程范式是解决此类问题的核心。
以上就是Android Studio中获取用户当前位置并显示在地图上的教程的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/9102.html
微信扫一扫
支付宝扫一扫