山东大学创新实训项目个人博客——第三篇
本周是项目开始开发的第四周主要完成了导航界面中高德API的集成工作。在 build.gradle.kts中配置依赖//build.gradle.kts(Module:app) implementation(com.amap.api:3dmap-location-search:latest.integration)在manifest中配置相关权限、设置好API key、声明定位服务:!-- 网络权限 -- uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE / !-- 定位权限 -- uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / meta-data android:namecom.amap.api.v2.apikey android:value在高德控制台中创建的api-key / !-- 在application标签下-- service android:namecom.amap.api.location.APSService /将高德官方下载的SDK内的so文件拷贝到与项目src目录同级的libs目录下发现x86_64文件夹为空说明高德官方已经不再支持在x86架构cpu上运行了也就意味着Android Studio内置的基于x86架构的虚拟机无法运行高德SDK也就意味着以后的开发不得不使用真机测试了复制完so文件后在build.gradle.kts中配置确保原生库在不同CPU架构设备上正常运行defaultConfig { applicationId com.example.wenkang minSdk 29 targetSdk 36 versionCode 1 versionName 1.0 testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner ndk { abiFilters listOf(armeabi-v7a, arm64-v8a, x86_64) } }在MainActivity的onCreate中配置如下参数告诉高德SDK我们已经得到了用户的许可才能开始使用高德SDK这里硬编码为true实际上应该询问用户后再设置为true// 地图SDK隐私合规 MapsInitializer.updatePrivacyShow(this, true, true) MapsInitializer.updatePrivacyAgree(this, true) // 定位SDK隐私合规 AMapLocationClient.updatePrivacyShow(this, true, true) AMapLocationClient.updatePrivacyAgree(this, true)将之前NavigationScreen中从ConsultationScreen复制过来的代码一并删除整个界面为一个Column分为上中下三个部分上部分占百分之五十为地图部分其余两部分占百分之二十五分别是查询出来的医院列表横向排布和列表中选中的医院的详情。这里主要介绍地图部分初始化MapViewval mapViewState remember { MapView(context).apply { setMapView(this) } }由于MapView是传统的Android View需要手动进行生命周期管理DisposableEffect(Unit) { mapViewState.onCreate(null) // 创建地图 mapViewState.onResume() // 恢复地图 onDispose { mapViewState.onPause() // 暂停地图 mapViewState.onDestroy() // 销毁地图 } }将其嵌入Compose中其中mapView.map 获取核心的 AMap 对象后续所有地图操作都基于它AndroidView( factory { mapViewState }, modifier Modifier.fillMaxSize() ) { mapView - val aMap mapView.map // 获取 AMap 对象 setAMap(aMap) // 设置地图点击事件 aMap.setOnMarkerClickListener { marker - val hospital markerHospitalMap[marker] hospital?.let { setSelectedHospital(it) } true } }以上是将高德地图MapView嵌入Compose的步骤用户在同意了权限后应该开启定位服务并将地图的定位中心切换到用户搜索周围的医院。在NavigationViewModel中写下获取用户定位方法大致的流程就是检查权限后进行定位如果定位不成功则使用默认定位这里由于项目要求定位成功后立刻调用了使用POI搜索医院的方法fun getCurrentLocation(context: Context? null) { context?.let { // 先检查定位权限权限验证不通过则使用默认位置 //使用try catch包裹定位失败也使用默认位置 try { locationClient AMapLocationClient(it) val option AMapLocationClientOption().apply { locationMode AMapLocationClientOption.AMapLocationMode.Hight_Accuracy isOnceLocation true isNeedAddress true // 获取详细地址信息 httpTimeOut 30000 // 设置超时时间30秒 isGpsFirst true // GPS优先 } locationClient?.setLocationOption(option) locationClient?.setLocationListener(object : AMapLocationListener { override fun onLocationChanged(location: AMapLocation) { if (location.errorCode 0) { val loc Location(AMap).apply { latitude location.latitude longitude location.longitude accuracy location.accuracy time location.time } _currentLocation.value loc // 定位成功后搜索附近医院 searchNearbyHospitals(context, location.latitude, location.longitude) } else { // 定位失败使用默认位置 val defaultLocation Location(Default).apply { latitude 39.916345 longitude 116.407174 } _currentLocation.value defaultLocation searchNearbyHospitals(context, defaultLocation.latitude, defaultLocation.longitude) } } }) locationClient?.startLocation() } catch (e: Exception) { e.printStackTrace() // 发生异常时使用默认位置 val defaultLocation Location(Default).apply { latitude 39.916345 longitude 116.407174 } _currentLocation.value defaultLocation searchNearbyHospitals(context, defaultLocation.latitude, defaultLocation.longitude) } } }而在UI层如果ViewModel成功获取到了用户定位UI层监听到后就会进行更新LaunchedEffect(currentLocation) { currentLocation?.let { location - val latLng LatLng(location.latitude, location.longitude) // 移动相机到用户位置 aMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f)) // 添加蓝色标记 aMap?.addMarker( MarkerOptions() .position(latLng) .title(我的位置) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE)) ) } }以上就是从用户打开界面、定位到用户位置到地图重定位并使用蓝色进行标记的全过程。POI搜索功能的实现大致相同同样是定位到用户位置后根据用户的位置去查询附近的医院并返回到hospitals中UI层监听到hospitals发生改变后即在地图上使用红色进行标记并将hospitals列表展示在下方的横向列表中列表的selected属性发生变化时切换地图的聚焦中点也就实现了追踪不同医院的效果// 搜索附近医院使用高德 POI 搜索 fun searchNearbyHospitals(context: Context? null, latitude: Double 39.916345, longitude: Double 116.407174) { if (context null) return try { // 查询条件关键词医院搜索类型为综合医院 val query PoiSearch.Query(三甲医院, , ) // 设置搜索范围 val searchBound PoiSearch.SearchBound( LatLonPoint(latitude, longitude), 100000 // 半径 ) query.pageSize 20 // 创建搜索实例 val poiSearch PoiSearch(context, query) poiSearch.bound searchBound poiSearch.setOnPoiSearchListener(object : PoiSearch.OnPoiSearchListener { override fun onPoiSearched(result: PoiResult?, code: Int) { if (code 1000 result ! null result.pois ! null) { val hospitals result.pois.map { poi - HospitalWithLocation( name poi.title ?: 未知医院, address poi.snippet ?: poi.adName ?: 地址未知, department 综合医院, level 三级甲等, rating 4.5f, // 默认评分 latitude poi.latLonPoint?.latitude ?: latitude, longitude poi.latLonPoint?.longitude ?: longitude ) } _hospitals.value hospitals } else { // 搜索失败时使用预设模拟数据作为保底 loadMockHospitals() } } override fun onPoiItemSearched(item: PoiItem?, code: Int) {} }) poiSearch.searchPOIAsyn() } catch (e: Exception) { e.printStackTrace() loadMockHospitals() } }// 监听医院列表变化更新地图标记 LaunchedEffect(hospitals) { // 清除旧标记 markers.forEach { it.remove() } setMarkers(emptyList()) setMarkerHospitalMap(emptyMap()) // 添加新标记 val newMarkers mutableListOfMarker() val newMarkerHospitalMap mutableMapOfMarker, HospitalWithLocation() hospitals.forEach { val latLng LatLng(it.latitude, it.longitude) val marker aMap?.addMarker( MarkerOptions() .position(latLng) .title(it.name) .snippet(it.address) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)) ) marker?.let {m - newMarkers.add(m) newMarkerHospitalMap[m] it } } setMarkers(newMarkers) setMarkerHospitalMap(newMarkerHospitalMap) }LazyRow( modifier Modifier.fillMaxWidth(), contentPadding PaddingValues(horizontal 16.dp) ) { items(hospitals) { HospitalCard( hospital it, isSelected selectedHospital?.name it.name, onSelect { setSelectedHospital(it) // 点击医院时地图移动到该医院位置 val latLng LatLng(it.latitude, it.longitude) aMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f)) } ) } }以上就是本项目集成高德SDK的过程用于定位用户并给用户推荐附近的三甲医院。不过后端的同学跟我说医院的推荐将由他们来完成他们会提供一个根据数据库推荐四家附近医院并返回医院更加详细信息的方法不需要调用高德POI搜索办法了后面我也会根据具体的后端实现进行修改。目前实现的效果如下接下来的工作是完成注册界面拖了两周了优化主界面的布局搬到真机上运行发现界面的排布还是有些问题以及完成导航阶段的前一个阶段——问诊阶段这部分后端的api已经实现我可以进行更具体的适配了比如实现流式输出等等。