You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Jetpack Compose中如何在Google地图标记旁显示文本

Jetpack Compose中如何在Google地图标记旁显示文本

你说得对,默认的Marker组件确实只会在点击时才弹出信息窗显示titlesnippet,要让文本一直和标记图标“绑在一起”显示,得我们自己动手做个带文本的自定义标记图标才行。下面我给你捋捋怎么解决这个问题,都是纯本地代码,不用跳去别的网站找资源~

最直接的方案:合成带文本的自定义Bitmap作为标记图标

我们可以写一个工具方法,把你的标记图标和要显示的文本画在同一张Bitmap上,然后把这个合成后的Bitmap设置给Marker的icon参数。这样地图上显示的就是自带文本的标记了。

首先写生成自定义图标的工具方法(可以放在你的Compose所在的Activity,或者单独的工具类里):

fun Context.createMarkerIconWithText(
    iconResId: Int,
    text: String,
    textSize: Float = 14f,
    textColor: Int = Color.BLACK,
    padding: Int = 8
): BitmapDescriptor {
    // 加载原始标记图标
    val originalIcon = BitmapFactory.decodeResource(resources, iconResId)
    // 初始化绘制文本的画笔
    val textPaint = Paint().apply {
        color = textColor
        textSize = textSize * resources.displayMetrics.scaledDensity
        isAntiAlias = true
        textAlign = Paint.Align.LEFT
    }
    // 计算文本的宽高
    val textBounds = Rect()
    textPaint.getTextBounds(text, 0, text.length, textBounds)
    // 计算合成后Bitmap的尺寸:图标宽+文本宽+边距,高度取图标和文本中较高的那个
    val totalWidth = originalIcon.width + textBounds.width() + padding * 2
    val totalHeight = max(originalIcon.height, textBounds.height() + padding * 2)
    // 创建新的空白Bitmap
    val combinedBitmap = Bitmap.createBitmap(totalWidth, totalHeight, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(combinedBitmap)
    // 绘制原始图标(垂直居中)
    val iconTop = (totalHeight - originalIcon.height) / 2f
    canvas.drawBitmap(originalIcon, 0f, iconTop, null)
    // 绘制文本(垂直居中,在图标右侧留边距)
    val textTop = (totalHeight + textBounds.height()) / 2f - textBounds.bottom
    canvas.drawText(
        text,
        originalIcon.width + padding.toFloat(),
        textTop,
        textPaint
    )
    // 转成Marker可用的BitmapDescriptor返回
    return BitmapDescriptorFactory.fromBitmap(combinedBitmap)
}

然后在你的Compose地图代码里,调用这个方法生成自定义图标:

GoogleMap(
    googleMapOptionsFactory = {
        GoogleMapOptions().mapId(MapDefaults.mapId)
    },
    cameraPositionState = cameraPositionState,
    properties = style.mapProperties,
    uiSettings = style.mapUiSettings,
    onMapClick = onMapClick,
    onMapLoaded = onMapLoaded,
) {
    val context = LocalContext.current
    // 用remember包裹,避免每次重组都重新生成Bitmap,优化性能
    val customMarkerIcon = remember {
        context.createMarkerIconWithText(
            iconResId = R.drawable.your_icon,
            text = "Text beside the icon"
        )
    }
    Marker(
        state = MarkerState(position = LatLng(latitude, longitude)),
        icon = customMarkerIcon
    )
}

小提示

  • 工具方法里的textSizetextColorpadding这些参数,你可以完全根据自己的UI需求调整
  • remember包裹customMarkerIcon是为了避免Compose重组时重复生成Bitmap,能省不少性能

备选思路(不推荐):强制显示信息窗

还有一种方法是通过InfoWindowAdapter自定义信息窗内容,然后主动调用marker.showInfoWindow()让它一直显示,但这种方法有个小问题:信息窗是悬浮在地图上的独立视图,和标记图标不是完全绑定的,缩放地图时可能会有偏移,体验不如第一种方法流畅,所以只作为备选提一下~

备注:内容来源于stack exchange,提问作者Pawandeep Singh

火山引擎 最新活动