.NET 8 MAUI中如何让谷歌地图Pin标签始终显示或添加底部文本?
我开发了一个.NET 8 MAUI移动端项目,已经实现了地图功能,用图标代表被追踪的实体设备,要求所有图标都显示设备ID文本。但创建Pin时发现,Pin的Label属性只有选中该Pin时才会显示,未选中就隐藏,而且Pin类里没有能让标签一直显示的属性。
添加Pin的代码示例:
foreach (var element in CollectionsMarshal.AsSpan(AppShell.ListaUnidades)) { var temp = new CustomPin() { Label = element.idtra, Location = new Location(Convert.ToDouble(element.paclat), Convert.ToDouble(element.paclon)), ImageSource = ImageSource.FromFile(ObtenerRumbo(element.pacrum) + element.Icono), }; MapaUnidades.Pins.Add(temp); }
CustomPin类继承自Pin,新增了ImageSource属性用来自定义Pin图标。我试过通过CustomMapHandler修改markerOptions来让标签一直显示,但只能控制整个Pin的可见性,代码示例如下:
foreach (var pin in mapPins) { var pinHandler = pin.ToHandler(MauiContext); if (pinHandler is IMapPinHandler mapPinHandler) { var markerOption = mapPinHandler.PlatformView; if (pin is CustomPin cp) { cp.ImageSource.LoadImage(MauiContext, result => { if (result?.Value is BitmapDrawable { Bitmap: not null } bitmapDrawable) { markerOption.SetIcon(BitmapDescriptorFactory.FromBitmap(GetMaximumBitmap(bitmapDrawable.Bitmap, 100, 100))); } AddMarker(Map, pin, markerOption); }); } else { AddMarker(Map, pin, markerOption); } } }
请问有没有办法让Pin标签始终显示?或者能不能在Pin下方加个文本(哪怕不属于Pin的一部分也行)?
Android平台
Android这边,你可以修改MarkerOptions的SetTitle和SetSnippet,设置InfoWindowAnchor,再强制让InfoWindow一直显示。更直接的办法是在创建Marker后调用ShowInfoWindow(),同时监听地图的CameraIdle事件,保证地图移动后重新显示InfoWindow:
在你的CustomMapHandler的AddMarker方法里,创建Marker后直接调用显示方法:
private Marker AddMarker(GoogleMap map, Pin pin, MarkerOptions markerOptions) { var marker = map.AddMarker(markerOptions); // 强制显示InfoWindow marker.ShowInfoWindow(); // 地图移动结束后重新显示,避免被隐藏 map.CameraIdle += (s, e) => marker.ShowInfoWindow(); return marker; }
要是想让标签和图标更贴合,还可以自定义InfoWindow的布局,替换默认样式:
- 在Android项目的Resources/layout目录下创建
custom_info_window.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:background="@android:color/transparent"> <TextView android:id="@+id/info_window_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="12sp" android:layout_gravity="center_horizontal"/> </LinearLayout>
- 在CustomMapHandler里设置自定义的InfoWindowAdapter:
map.SetInfoWindowAdapter(new CustomInfoWindowAdapter(MauiContext.Context)); // 自定义InfoWindowAdapter类 public class CustomInfoWindowAdapter : Java.Lang.Object, GoogleMap.IInfoWindowAdapter { private readonly View _infoWindow; public CustomInfoWindowAdapter(Context context) { _infoWindow = LayoutInflater.From(context).Inflate(Resource.Layout.custom_info_window, null); } public View GetInfoContents(Marker marker) { var titleTextView = _infoWindow.FindViewById<TextView>(Resource.Id.info_window_title); titleTextView.Text = marker.Title; return _infoWindow; } public View GetInfoWindow(Marker marker) { return null; // 使用GetInfoContents返回的布局 } }
iOS平台
iOS上,MKMarkerAnnotationView默认只在选中时显示标题,要让标题一直显示,得自定义MKAnnotationView,把CanShowCallout设为true,再添加一个UILabel作为子视图放在图标下方:
在iOS项目的CustomMapHandler中处理AnnotationView时:
protected override MKAnnotationView CreateMarker(Pin pin) { if (pin is CustomPin customPin) { var annotationView = new MKAnnotationView(customPin, "CustomPin"); // 设置自定义图标 if (customPin.ImageSource is FileImageSource fileSource) { annotationView.Image = UIImage.FromFile(fileSource.File); } // 添加标题标签 var titleLabel = new UILabel { Text = customPin.Label, Font = UIFont.SystemFontOfSize(12), TextColor = UIColor.Black, BackgroundColor = UIColor.Clear, TextAlignment = UITextAlignment.Center, AutoresizingMask = UIViewAutoresizing.FlexibleWidth }; // 把标签放在图标下方,调整位置 titleLabel.Frame = new CGRect(0, annotationView.Image.Size.Height + 2, annotationView.Image.Size.Width, 20); annotationView.AddSubview(titleLabel); return annotationView; } return base.CreateMarker(pin); }
这样标签会直接作为自定义AnnotationView的子视图,始终显示在图标下方,不用依赖Callout。
跨平台通用方案(自定义Overlay)
要是不想分平台折腾,也可以用地图的Overlay功能,在每个Pin的位置单独绘制文本:
- 先创建一个自定义的MapOverlay类,包含位置和文本信息:
public class TextOverlay : IOverlay { public Location Location { get; set; } public string Text { get; set; } }
- 在CustomMapHandler中,把这些TextOverlay转换成平台原生的Overlay:
- Android:可以用GroundOverlay或者Canvas绘制文本
- iOS:用MKOverlayRenderer绘制文本
这种方式的好处是文本完全独立于Pin,不受Pin的交互影响,一直显示。
内容的提问来源于stack exchange,提问作者Eli Sal

