Android Osmdroid + 天地图 (二)

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)
	})

添加位置如下图所示:

运行效果如下图所示:

六、源码

如果对你有所帮助的话,不妨欢迎StarFork

源码地址:OpenMap

APK下载地址:OpenMap1.0.apk

相关推荐
CYRUS_STUDIO8 小时前
Frida 检测与对抗实战:进程、maps、线程、符号全特征清除
android·逆向
csj509 小时前
安卓基础之《(28)—Service组件》
android
lhbian11 小时前
PHP、C++和C语言对比:哪个更适合你?
android·数据库·spring boot·mysql·kafka
catoop12 小时前
Android 最佳实践、分层架构与全流程解析(2025)
android
ZHANG13HAO12 小时前
Android 13 特权应用(Android Studio 开发)调用 AOSP 隐藏 API 完整教程
android·ide·android studio
田梓燊12 小时前
leetcode 142
android·java·leetcode
angerdream13 小时前
Android手把手编写儿童手机远程监控App之JAVA基础
android
菠萝地亚狂想曲13 小时前
Zephyr_01, environment
android·java·javascript
sTone8737513 小时前
跨端框架通信机制全解析:从 URL Schema 到 JSI 到 Platform Channel
android·前端
sTone8737513 小时前
Java 注解完全指南:从 "这是什么" 到 "自己写一个"
android·前端