一.引言

Android原生开发快速接入高德地图,本文简单就显示地图和获取POI搜索数据进行简单的讲解。如果你觉得本文对你有帮助,来一波点赞关注赞赏,谢谢!

二.接入流程

1.在官网注册,获取key

获取Key-创建工程-开发指南-Android 地图SDK | 高德地图API

2.从官网下载资源并在项目中导入

相关下载-Android 地图SDK | 高德地图API

3.项目配置

在AndroidManifest.xml文件中配置(将key替换为你从官网获取的key值

     <meta-data android:name="com.amap.api.v2.apikey" android:value="key">
        </meta-data>

设置权限

    <!--允许访问网络,必选权限-->
    <uses-permission android:name="android.permission.INTERNET" />

    <!--允许获取粗略位置,若用GPS实现定位小蓝点功能则必选-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!--用于访问GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!--用于提高GPS定位速度-->
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />

    <!--允许获取网络状态,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!--允许获取wifi网络信息,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!--允许获取wifi状态改变,用于网络定位,若无gps但仍需实现定位小蓝点功能则此权限必选-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!--允许写入扩展存储,用于数据缓存,若无此权限则写到私有目录-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />

4.代码实现

class MainActivity : ComponentActivity(), PoiSearch.OnPoiSearchListener, LocationSource, AMap.OnMyLocationChangeListener {
    // 定义定位权限请求的代码
    private val LOCATION_PERMISSION_REQUEST_CODE = 1001
    private lateinit var poiSearch: PoiSearch
    private var poiResult: PoiResult? = null
    private var poiItems: List<PoiItem>? = null
    private var isLoading by mutableStateOf(false)
    private var poiDetails by mutableStateOf<List<PoiItem>>(emptyList())

    // 地图视图和 AMap 相关引用
    private lateinit var mapView: MapView
    private lateinit var aMap: AMap
    private var locationMarker: Marker? = null
    private var locationClient: AMapLocationClient? = null
    private lateinit var locationOption: AMapLocationClientOption

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 设置高德地图的 API Key
        setApiKey("************************")

        // 检查定位权限,如果未授予则请求权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE)
        } else {
            // 如果权限已授予,初始化地图和定位功能
            initializeMapAndLocation()
        }

        // Jetpack Compose 的UI内容
        setContent {
            // 显示搜索 POI 的界面
            PoiSearchScreen()
        }
    }

    // 初始化地图和定位功能
    private fun initializeMapAndLocation() {
        mapView = MapView(this)
        mapView.onCreate(null) // 必须在生命周期内调用
        aMap = mapView.map

        setupLocationStyle()
        aMap.setLocationSource(this)
        aMap.isMyLocationEnabled = true
        aMap.setOnMyLocationChangeListener(this)
    }

    // 搜索 POI 的界面
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun PoiSearchScreen() {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
        ) {
            Column {
                Button(
                    onClick = {
                        isLoading = true
                        performNearbyPoiSearch()// 执行 POI 搜索
                    },
                    modifier = Modifier.fillMaxWidth().zIndex(2f)
                ) {
                    Text("搜索附近的 POI")
                }

                Spacer(modifier = Modifier.height(16.dp))

                if (isLoading) {
                    CircularProgressIndicator()
                }
                LazyColumn(modifier = Modifier.fillMaxWidth()) {
                    items(poiDetails) { poiItem ->
                        PoiItemView(poiItem) { latLng ->
                            // 在点击 POI 时,更新地图标记的位置并聚焦
                            updateMapWithPoiLocation(latLng)
                        } // 显示每个 POI 的详情
                    }
                }
                // 地图视图的容器
                AndroidView(factory = { mapView }) {
                    it.onResume() // 必须确保地图生命周期同步
                }
            }
        }
    }

    // 定义POI条目的UI
    @Composable
    fun PoiItemView(poiItem: PoiItem,onClick: (LatLng) -> Unit) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
                .clickable {
                    val latLng = LatLng(poiItem.latLonPoint.latitude, poiItem.latLonPoint.longitude)
                    onClick(latLng) // 点击时传递 POI 的坐标
                }
        ) {
            val imageUrl = poiItem.photos?.getOrNull(0)?.url // 获取第一个照片
            if (imageUrl != null) {
                Image(
                    painter = rememberAsyncImagePainter(imageUrl),
                    contentDescription = "POI 图片",
                    modifier = Modifier.size(80.dp),
                    contentScale = ContentScale.Crop
                )
            }
            Spacer(modifier = Modifier.width(8.dp))
            Column {
                Text(poiItem.title, style = MaterialTheme.typography.bodyMedium)
                Text(poiItem.snippet,  style = MaterialTheme.typography.bodyMedium)
                Text(poiItem.tel,  style = MaterialTheme.typography.bodyMedium)
            }
        }
    }

    // 设置蓝点样式
    private fun setupLocationStyle() {
        val myLocationStyle = MyLocationStyle()
            .myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW) // 设置蓝点跟随模式
            .interval(2000) // 每2秒更新一次位置
            .strokeColor(R.color.purple_200) // 蓝点精度圈边框颜色
            .radiusFillColor(R.color.purple_500) // 蓝点精度圈填充颜色
            .strokeWidth(2f) // 精度圈边框宽度

        aMap.setMyLocationStyle(myLocationStyle)
        aMap.uiSettings.isMyLocationButtonEnabled = true // 显示定位按钮
    }

    // 执行附近 POI 搜索
    private fun performNearbyPoiSearch() {
        val location = locationMarker?.position
        if (location != null) {
            val latLonPoint = LatLonPoint(location.latitude, location.longitude)
//keyWord表示搜索字符串,
//第二个参数表示POI搜索类型,二者选填其一,选用POI搜索类型时建议填写类型代码,码表可以参考下方(而非文字)
//cityCode表示POI搜索区域,可以是城市编码也可以是城市名称,也可以传空字符串,空字符串代表全国在全国范围内进行搜索
            val query = PoiSearch.Query("自习室", "", "")
            query.extensions = "all"
            query.pageSize = 3
            query.pageNum = 0

            poiSearch = PoiSearch(this, query)
            poiSearch.bound = PoiSearch.SearchBound(latLonPoint, 10000)
            poiSearch.setOnPoiSearchListener(this)
            poiSearch.searchPOIAsyn()
        } else {
            isLoading = false
            poiDetails = emptyList()
        }
    }

    // POI 搜索结果回调
    override fun onPoiSearched(result: PoiResult?, rCode: Int) {
        isLoading = false
        if (rCode == 1000 && result != null) {
            poiResult = result
            poiItems = result.pois

            // 更新POI列表
            poiDetails = poiItems ?: emptyList()

            // 清除旧的地图标记
            aMap.clear()

            // 为每个 POI 添加标记
            poiItems?.forEach { poi ->
                val latLng = LatLng(poi.latLonPoint.latitude, poi.latLonPoint.longitude)
                aMap.addMarker(MarkerOptions().position(latLng).title(poi.title))
            }
        } else {
            poiDetails = emptyList()
        }
    }
    // 定位激活时的回调
    override fun activate(listener: LocationSource.OnLocationChangedListener?) {
        locationClient = AMapLocationClient(applicationContext)
        locationOption = AMapLocationClientOption().apply {
            locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
            interval = 2000
        }

        locationClient?.setLocationListener { location ->
            listener?.onLocationChanged(location)
            val latLng = LatLng(location.latitude, location.longitude)
            if (locationMarker == null) {
                locationMarker = aMap.addMarker(MarkerOptions().position(latLng).title("当前位置"))
            } else {
                locationMarker?.position = latLng
            }
        }

        locationClient?.setLocationOption(locationOption)
        locationClient?.startLocation()
    }
    // 定位停用时的回调
    override fun deactivate() {
        locationClient?.stopLocation()
        locationClient?.onDestroy()
        locationClient = null
    }

    // 我的位置变化时的回调(可选实现)
    override fun onMyLocationChange(location: Location?) {}
    // 单个 POI 条目搜索回调(此处暂未使用)
    override fun onPoiItemSearched(poiItem: PoiItem?, rCode: Int) {
    }
    // 更新地图标记位置并聚焦
    private fun updateMapWithPoiLocation(latLng: LatLng) {
        aMap.clear() // 清除旧的标记
        aMap.addMarker(MarkerOptions().position(latLng).title("当前位置")) // 添加新的标记
        aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15f)) // 聚焦到该位置,缩放级别为15
    }
}

5.注意

query.setExtensions("all")

这是关键,因为搜索SDK自7.6.0开始增加了setExtension() 方法控制深度信息,扩展字段 base表示只返回基础数据,all表示所有数据,默认为base,可以通过query设置all,获取到完整的地址信息。

6.效果展示图

点击item,地图会更新。如果拓展需要其他Poiitem类型,请查看官方文档

Android SDK

三.结尾

以上就是我在Compose中对高德地图的简单接入。你可以在此基础上进行拓展,一定要注意处理好生命周期,否则会出错。如果你有任何疑问,可以在评论区讨论。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐