在 kotlin 中使用 MapView 检索片段中的当前位置?
Retrieve the current Location in fragment with MapView in kotlin?
我想检索当前位置并在用户移动时更新他的位置。
我在创建地图时有一个 mapView。现在我的问题是如何获取用户当前位置,如果他移动位置是否更新?由于 ApiLevel,我使用 googleApliclient 并使用 Mapview。
我可以检索权限,但遗憾的是我不知道如何检索用户位置并更新它。也欢迎对代码进行改进。
class MapFragment : Fragment(), OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private var mapView: MapView? = null
private var gMap: GoogleMap? = null
private val LOCATION_PERMISSION_REQUEST_CODE = 1234
private lateinit var mapViewModel: MapViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mapViewModel =
ViewModelProvider(this).get(MapViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_map, container, false)
mapView = root.findViewById(R.id.mapView) as MapView
if (mapView != null) {
mapView!!.onCreate(null);
mapView!!.onResume();
mapView!!.getMapAsync(this);
checkLocationPermission();
}
checkLocationPermission();
return root
}
override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onPause() {
super.onPause()
mapView?.onPause()
}
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap) {
MapsInitializer.initialize(context)
gMap = googleMap
val coffeys = LatLng(52.075890, 4.309170)
gMap!!.addMarker(MarkerOptions().position(coffeys).title("coffeys"))
gMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(coffeys, 12f))
}
fun checkLocationPermission(): Boolean {
return if (ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
!= PackageManager.PERMISSION_GRANTED
) {
// Asking user if explanation is needed
if (ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
//Prompt the user once explanation has been shown
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
} else {
// No explanation needed, we can request the permission.
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
false
} else {
true
}
}
override fun onConnected(p0: Bundle?) {
}
override fun onConnectionSuspended(p0: Int) {
TODO("Not yet implemented")
}
override fun onConnectionFailed(p0: ConnectionResult) {
TODO("Not yet implemented")
}
}
使用融合位置提供程序检索设备的最后已知位置。
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// ...
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
请求最后已知位置并处理响应:
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
// Got last known location. In some rare situations this can be null.
}
此 returns 任务可用于获取具有地理位置经纬度坐标的 Location 对象。
更多详情:
https://developer.android.com/training/location/request-updates
更新:
完成实施
@SuppressLint("MissingPermission")
private fun getDeviceLocation() {
/*
* Get the best and most recent location of the device, which may be null in rare
* cases when a location is not available.
*/
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient.lastLocation
locationResult.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
Timber.d("last known location $lastKnownLocation")
map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(
lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude
), DEFAULT_ZOOM.toFloat()
)
)
} else {
Timber.e( "Exception: %s", task.exception)
map.moveCamera(
CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat())
)
map.uiSettings?.isMyLocationButtonEnabled = false
}
}
}
}
} catch (e: SecurityException) {
Timber.e("Exception: %s", e.message)
}
}
更新 2:位置更新
此代码来自官方文档,为 activity 实现。应该也适用于片段。注意:我没有测试过这个。
调用 requestLocationUpdates()
,将您的 locationRequest
对象实例和 locationCallback
传递给它。定义一个 startLocationUpdates()
方法,如以下代码示例所示:
override fun onResume() {
super.onResume()
if (requestingLocationUpdates) startLocationUpdates()
}
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper())
}
融合位置提供程序调用 LocationCallback.onLocationResult()
回调方法。传入参数包含一个列表 Location 对象,其中包含位置的纬度和经度。以下代码片段展示了如何实现 LocationCallback
接口并定义方法,然后获取位置更新的时间戳并在应用程序的用户界面上显示纬度、经度和时间戳:
private lateinit var locationCallback: LocationCallback
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations){
// Update UI with location data
// ...
}
}
}
}
要停止位置更新,请调用 removeLocationUpdates()
,将其传递给 locationCallback
,如以下代码示例所示:
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
设备配置的更改,例如屏幕方向或语言的更改,可能会导致当前 activity 被破坏。因此,您的应用必须存储重新创建 activity 所需的任何信息。一种方法是通过存储在 Bundle 对象中的实例状态。
以下代码示例显示了如何使用 activity 的 onSaveInstanceState()
回调来保存实例状态:
override fun onSaveInstanceState(outState: Bundle?) {
outState?.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, requestingLocationUpdates)
super.onSaveInstanceState(outState)
}
定义一个 updateValuesFromBundle()
方法来从 activity 的先前实例中恢复保存的值(如果它们可用)。从 activity 的 onCreate()
方法调用方法,如以下代码示例所示:
override fun onCreate(savedInstanceState: Bundle?) {
// ...
updateValuesFromBundle(savedInstanceState)
}
private fun updateValuesFromBundle(savedInstanceState: Bundle?) {
savedInstanceState ?: return
// Update the value of requestingLocationUpdates from the Bundle.
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
requestingLocationUpdates = savedInstanceState.getBoolean(
REQUESTING_LOCATION_UPDATES_KEY)
}
// ...
// Update UI to match restored state
updateUI()
}
为了更持久的存储,您可以将用户的首选项存储在您应用的 SharedPreferences
中。在 activity 的 onPause()
方法中设置共享首选项,并在 onResume()
中检索首选项
我想检索当前位置并在用户移动时更新他的位置。 我在创建地图时有一个 mapView。现在我的问题是如何获取用户当前位置,如果他移动位置是否更新?由于 ApiLevel,我使用 googleApliclient 并使用 Mapview。 我可以检索权限,但遗憾的是我不知道如何检索用户位置并更新它。也欢迎对代码进行改进。
class MapFragment : Fragment(), OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private var mapView: MapView? = null
private var gMap: GoogleMap? = null
private val LOCATION_PERMISSION_REQUEST_CODE = 1234
private lateinit var mapViewModel: MapViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mapViewModel =
ViewModelProvider(this).get(MapViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_map, container, false)
mapView = root.findViewById(R.id.mapView) as MapView
if (mapView != null) {
mapView!!.onCreate(null);
mapView!!.onResume();
mapView!!.getMapAsync(this);
checkLocationPermission();
}
checkLocationPermission();
return root
}
override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onPause() {
super.onPause()
mapView?.onPause()
}
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap) {
MapsInitializer.initialize(context)
gMap = googleMap
val coffeys = LatLng(52.075890, 4.309170)
gMap!!.addMarker(MarkerOptions().position(coffeys).title("coffeys"))
gMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(coffeys, 12f))
}
fun checkLocationPermission(): Boolean {
return if (ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
!= PackageManager.PERMISSION_GRANTED
) {
// Asking user if explanation is needed
if (ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
//Prompt the user once explanation has been shown
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
} else {
// No explanation needed, we can request the permission.
requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
}
false
} else {
true
}
}
override fun onConnected(p0: Bundle?) {
}
override fun onConnectionSuspended(p0: Int) {
TODO("Not yet implemented")
}
override fun onConnectionFailed(p0: ConnectionResult) {
TODO("Not yet implemented")
}
}
使用融合位置提供程序检索设备的最后已知位置。
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// ...
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
请求最后已知位置并处理响应:
fusedLocationClient.lastLocation
.addOnSuccessListener { location : Location? ->
// Got last known location. In some rare situations this can be null.
}
此 returns 任务可用于获取具有地理位置经纬度坐标的 Location 对象。
更多详情: https://developer.android.com/training/location/request-updates
更新: 完成实施
@SuppressLint("MissingPermission")
private fun getDeviceLocation() {
/*
* Get the best and most recent location of the device, which may be null in rare
* cases when a location is not available.
*/
try {
if (locationPermissionGranted) {
val locationResult = fusedLocationProviderClient.lastLocation
locationResult.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful) {
// Set the map's camera position to the current location of the device.
lastKnownLocation = task.result
if (lastKnownLocation != null) {
Timber.d("last known location $lastKnownLocation")
map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(
lastKnownLocation!!.latitude,
lastKnownLocation!!.longitude
), DEFAULT_ZOOM.toFloat()
)
)
} else {
Timber.e( "Exception: %s", task.exception)
map.moveCamera(
CameraUpdateFactory
.newLatLngZoom(defaultLocation, DEFAULT_ZOOM.toFloat())
)
map.uiSettings?.isMyLocationButtonEnabled = false
}
}
}
}
} catch (e: SecurityException) {
Timber.e("Exception: %s", e.message)
}
}
更新 2:位置更新
此代码来自官方文档,为 activity 实现。应该也适用于片段。注意:我没有测试过这个。
调用 requestLocationUpdates()
,将您的 locationRequest
对象实例和 locationCallback
传递给它。定义一个 startLocationUpdates()
方法,如以下代码示例所示:
override fun onResume() {
super.onResume()
if (requestingLocationUpdates) startLocationUpdates()
}
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper())
}
融合位置提供程序调用 LocationCallback.onLocationResult()
回调方法。传入参数包含一个列表 Location 对象,其中包含位置的纬度和经度。以下代码片段展示了如何实现 LocationCallback
接口并定义方法,然后获取位置更新的时间戳并在应用程序的用户界面上显示纬度、经度和时间戳:
private lateinit var locationCallback: LocationCallback
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations){
// Update UI with location data
// ...
}
}
}
}
要停止位置更新,请调用 removeLocationUpdates()
,将其传递给 locationCallback
,如以下代码示例所示:
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
设备配置的更改,例如屏幕方向或语言的更改,可能会导致当前 activity 被破坏。因此,您的应用必须存储重新创建 activity 所需的任何信息。一种方法是通过存储在 Bundle 对象中的实例状态。
以下代码示例显示了如何使用 activity 的 onSaveInstanceState()
回调来保存实例状态:
override fun onSaveInstanceState(outState: Bundle?) {
outState?.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, requestingLocationUpdates)
super.onSaveInstanceState(outState)
}
定义一个 updateValuesFromBundle()
方法来从 activity 的先前实例中恢复保存的值(如果它们可用)。从 activity 的 onCreate()
方法调用方法,如以下代码示例所示:
override fun onCreate(savedInstanceState: Bundle?) {
// ...
updateValuesFromBundle(savedInstanceState)
}
private fun updateValuesFromBundle(savedInstanceState: Bundle?) {
savedInstanceState ?: return
// Update the value of requestingLocationUpdates from the Bundle.
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
requestingLocationUpdates = savedInstanceState.getBoolean(
REQUESTING_LOCATION_UPDATES_KEY)
}
// ...
// Update UI to match restored state
updateUI()
}
为了更持久的存储,您可以将用户的首选项存储在您应用的 SharedPreferences
中。在 activity 的 onPause()
方法中设置共享首选项,并在 onResume()