Osmdroid + 天地图 (二)
前言
上一篇中我们显示了地图,但是还不够,不满足基本的使用情况,本篇中继续进行功能使用上的完善。
正文
本文中要实现定位和地图的交互功能,还有一些体验上的功能,首先我们先实现定位功能,意思就是一打开地图就定位到当前所在的位置。
一、定位监听
Android实际上有自带的定位监听,位置准不准两说,起码是有的,下面我们来使用一下,在MainActivity中增加如下代码:
kotlin
private val TAG = "MainActivity"
// 是否定位
private var isLocation = false
// 定位管理器
private lateinit var locationManager: LocationManager
一个用于打印、一个用于控制是否定位,locationManager
我们就在onCreate()
函数中进行实例化,如下所示:
kotlin
// 创建位置管理器实例
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
代码位置如下图所示
然后实现一个Android原生的定位监听,代码如下所示:
kotlin
private val locationListener = LocationListener { location -> // 处理位置变化
val latitude = location.latitude
val longitude = location.longitude
Log.d(TAG, "onLocationChanged: $latitude, $longitude")
}
然后我们可以写一个开始和停止定位的函数,代码如下所示:
kotlin
private var isLocation = false
private fun startLocation() {
if (!isLocation){
// 注册位置监听器
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, locationListener)
isLocation = !isLocation
}
}
private fun stopLocation() {
if (isLocation) {
// 停止位置更新
locationManager.removeUpdates(locationListener)
isLocation = !isLocation
}
}
你可能会发现,这行locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, locationListener)
代码报错,我们看看源码是怎么样的
这里告诉我们调用这个方法需要在请求了定位权限之后,否则就会闪退,不信你可以试试看,而我们现在明显已经是获取了权限了,那么我们也可以增加一个注解,点击这一行,Alt + Enter,出现弹窗。
选择标注的这一项,则会在方法上方添加一个注解:@SuppressLint("MissingPermission")
,这并不是最好的方式,但是省事,只要你满足那个前提,那就不会有问题,最后我们在initMap()函数中调用startLocation()
,如下图所示
同时我们在定位监听回调中调用stopLocation()
,如下图所示
下面我们运行一下看看是否会触发定位,看看控制台是否会打印经纬度。
打印了出来,这证明定位监听是有效的,下面我们需要改变地图中心。
二、改变地图中心
在MainActivity中增加一个函数,代码如下所示:
kotlin
/**
* 修改地图中心点
*/
private fun changeMapCenter(geoPoint: GeoPoint) {
Log.d(TAG, "changeMapCenter: $geoPoint")
binding.mapView.apply {
controller.apply {
setZoom(14.0)
setCenter(geoPoint)
}
}
}
当调用changeMapCenter时,打印一下然后通过地图控制器修改缩放比例和地图中心,接下来就在定位监听中回调中调用changeMapCenter()
,如下图所示:
下面运行一下,注意定位可能会有点慢,请耐心等待,只要定位成功了地图肯定会改变的,我们通过日志确认一下:
虽然我们改变了地图中心,但是没有标识,都不知道是哪里,下面就添加一个Marker。
三、添加Marker
首先在MainActivity
中声明一个变量
kotlin
// 标记
private var mMarker: Marker? = null
然后我们修改changeMapCenter()
函数,添加代码如下所示:
kotlin
if (mMarker != null) {
overlays.remove(mMarker)
}
mMarker = Marker(this).apply {
title = "Marker"
position = geoPoint
}
// 添加标点
overlays.add(mMarker)
添加位置如下图所示:
此时你再运行一下就能看到一个标点了,我就不贴图了,容易暴露位置被Gank。
四、地图点击
下面我们来做一个地图点击事件,地图点击是在OverlayManager
上完成的,我们回到initMap()
函数,增加如下代码:
kotlin
// 覆盖管理器配置
overlayManager.apply {
tilesOverlay.isEnabled = true
add(object : Overlay() {
override fun onSingleTapConfirmed(e: MotionEvent?, mapView: MapView?): Boolean {
Log.d(TAG, "onSingleTapConfirmed")
return super.onSingleTapConfirmed(e, mapView)
}
})
}
添加位置如下图所示:
从上述代码来看,我们启用地图上的图块叠加层,并添加一个新的叠加层,该叠加层在单击时打印日志,下面运行一下随便点击,看看控制台是否有日志打印。
出现了日志说明点击有效果,实际上还有一个方法,如下所示:
kotlin
override fun onSingleTapUp(e: MotionEvent?, mapView: MapView?): Boolean {
Log.d(TAG, "onSingleTapUp")
return super.onSingleTapUp(e, mapView)
}
这个函数也是单击,只不过我在测试的时候,同时打印时发现,每次点击这两个都会触发,而onSingleTapConfirmed()
是最后触发的,所以就用onSingleTapConfirmed()
了。现在点击生效之后,我们需要在点击之后改变地图位置,那么就可以调用changeMapCenter()
函数,但是它需要传入一个GeoPoint
对象,因此我们需要通过mapView去得到这个对象所需要的值,也就是经纬度,下面我们在onSingleTapConfirmed()
回调中,增加如下所示代码:
kotlin
// 获取投影对象后进行坐标转换再切换地图中心位置
mapView?.projection?.let { proj ->
val geoPoint = proj.fromPixels(e!!.x.toInt(), e.y.toInt()) as GeoPoint
Log.d(TAG, "onSingleTapConfirmed: 切换地图中心位置")
changeMapCenter(geoPoint)
}
添加位置如下图所示:
这段代码的含义通过上面的注释应该都清楚了,再通俗一点,就是点击屏幕的像素进行x,y坐标的转换,下面再运行一下看看会怎么样?
看到这个日志地图就已经切换成功了。
五、其他配置
地图上还有一些其他的配置,比如我们可以显示缩放控件。
① 缩放控件
通过zoomController去控制显示的状态。
kotlin
zoomController.setVisibility(Visibility.SHOW_AND_FADEOUT)
比如这里我们设置为SHOW_AND_FADEOUT
,就是淡入淡出,当你点击触摸屏幕时就会在底部出现,不触摸屏幕3.5s后控件消失,还有两个属性是ALWAYS, NEVER
,很好理解就是总是显示和从不显示的意思,我们之前的代码中是设置从不显示的,你可以改成SHOW_AND_FADEOUT
。
② Marker更换图标
我们可以通过marker的属性去更改图标,首先我们画一个图标,在drawable下新建一个ic_marker.xml文件,代码如下所示:
xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="#5AD3E5"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2L12,2C8.13,2 5,5.13 5,9c0,1.74 0.5,3.37 1.41,4.84c0.95,1.54 2.2,2.86 3.16,4.4c0.47,0.75 0.81,1.45 1.17,2.26C11,21.05 11.21,22 12,22h0c0.79,0 1,-0.95 1.25,-1.5c0.37,-0.81 0.7,-1.51 1.17,-2.26c0.96,-1.53 2.21,-2.85 3.16,-4.4C18.5,12.37 19,10.74 19,9C19,5.13 15.87,2 12,2zM12,11.75c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5S13.38,11.75 12,11.75z" />
</vector>
然后通过一行代码去设置,如下所示:
kotlin
icon = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_marker)
位置如下图所示:
这样我们就替换掉了默认的那个Marker图标。
③ 添加比例尺
在地图上添加比例尺,在initMap()
中,添加代码如下所示:
kotlin
add(ScaleBarOverlay(binding.mapView).apply {
setAlignBottom(true) // 底部对齐
setScaleBarOffset(100, 10) // 设置偏移量
})
添加位置如下图所示:
这里的setAlignBottom()
设置显示在屏幕底部,还有两个方法:setCentred()、setAlignRight()
,根据方法名可以知道是什么意思,自行测试。
④ 添加指南针
添加指南针
kotlin
// 添加指南针
add(CompassOverlay(this@MainActivity, binding.mapView).apply {
enableCompass()
})
添加位置如下图所示:
⑤ 添加经纬度网格线
添加 显示纬度/经度网格线
kotlin
// 添加经纬度网格线
add(LatLonGridlineOverlay2())
添加位置如下图所示:
⑥ 启用旋转手势
启用旋转手势需要配置地图和增加叠加层,地图上设置:
kotlin
setMultiTouchControls(true)
添加叠加层
kotlin
// 启用旋转手势
add(RotationGestureOverlay(binding.mapView).apply { isEnabled = true })
添加位置如下图所示:
⑦ 添加小地图
通过小地图叠加层添加,根据屏幕的宽高 / 4设置小地图的宽高,并且设置小地图瓦片资源,代码如下所示:
kotlin
add(MinimapOverlay(this@MainActivity, binding.mapView.tileRequestCompleteHandler).apply {
val dm = resources.displayMetrics
width = dm.widthPixels / 4
height = dm.heightPixels / 4
// 设置小地图资源
setTileSource(Config.TDTCIA_W)
})
添加位置如下图所示:
运行效果如下图所示:
六、源码
如果对你有所帮助的话,不妨欢迎Star
和Fork
。
源码地址:OpenMap
APK下载地址:OpenMap1.0.apk