You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI Android自定义Map Pin上方绘制文本问题求助

问题:在MAUI地图Pin上方添加文本

我需要在地图Pin的上方绘制文本,尝试用Canvas绘制但未成功。现有代码如下,求解决方案。

当前效果

当前效果

期望效果

期望效果


现有代码

CustomMapHandler.cs

public class CustomMapHandler : MapHandler
{
    public static readonly IPropertyMapper<IMap, IMapHandler> CustomMapper =
        new PropertyMapper<IMap, IMapHandler>(Mapper)
        {
            [nameof(IMap.Pins)] = MapPins,
        };

    public CustomMapHandler() : base(CustomMapper, CommandMapper)
    {
    }

    public CustomMapHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null) : base(
        mapper ?? CustomMapper, commandMapper ?? CommandMapper)
    {
    }

    public List<Marker> Markers { get; } = new();

    protected override void ConnectHandler(MapView platformView)
    {
        base.ConnectHandler(platformView);
        var mapReady = new MapCallbackHandler(this);
        PlatformView.GetMapAsync(mapReady);
    }

    private static new void MapPins(IMapHandler handler, IMap map)
    {
        if (handler is CustomMapHandler mapHandler)
        {
            mapHandler.Markers.Clear();
            mapHandler.AddPins(map.Pins);
        }
    }

    private void AddPins(IEnumerable<IMapPin> mapPins)
    {
        if (Map is null || MauiContext is null)
        {
            return;
        }

        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 bitmapDrawable)
                        {
                            markerOption.SetIcon(BitmapDescriptorFactory.FromBitmap(bitmapDrawable.Bitmap));
                        }

                        AddMarker(Map, pin, Markers, markerOption);
                    });
                }
                else
                {
                    AddMarker(Map, pin, Markers, markerOption);
                }
            }
        }
    }

    private static void AddMarker(GoogleMap map, IMapPin pin, List<Marker> markers, MarkerOptions markerOption)
    {
        var marker = map.AddMarker(markerOption);
        pin.MarkerId = marker.Id;
        markers.Add(marker);
    }
}

MapCallbackHandler.cs

class MapCallbackHandler : Java.Lang.Object, IOnMapReadyCallback
{
    private readonly IMapHandler mapHandler;

    public MapCallbackHandler(IMapHandler mapHandler)
    {
        this.mapHandler = mapHandler;
    }

    public void OnMapReady(GoogleMap googleMap)
    {
        mapHandler.UpdateValue(nameof(IMap.Pins));
    }
}

CustomPin.cs

public class CustomPin : Pin
{
    public static readonly BindableProperty ImageSourceProperty =
        BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(CustomPin));

    public ImageSource? ImageSource
    {
        get => (ImageSource?)GetValue(ImageSourceProperty);
        set => SetValue(ImageSourceProperty, value);
    }
}

MainPage.xaml.cs

void insertOnMap()
{
    var customPinFromResource = new CustomPin()
    {
       
        Label = "From Resource",
        Location = new Location(48.95049, 2.62891),
        Address = "Address3",
        ImageSource = ImageSource.FromFile("map_large_vertical_stopped_marker.png"),
        Map = map
    };
    map.Pins.Add(customPinFromResource);
    map.MoveToRegion(new MapSpan(new Location(48.95049, 2.62891), 48.95049, 2.62891));

}

MainPage.Xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
             xmlns:charts="clr-namespace:TestMap.Charts"
             x:Class="TestMap.MainPage">
    
        <VerticalStackLayout>
            <Map x:Name="map" HeightRequest="400" MapType="Street" IsVisible="true"/>
        
    </VerticalStackLayout>
    

</ContentPage>

解决方案

核心思路是将文本绘制到Pin的图标Bitmap上,合成新的Bitmap作为Marker的Icon,具体步骤如下:

1. 扩展CustomPin添加顶部文本属性

在CustomPin.cs中新增绑定属性,用于存储要显示在Pin上方的文本:

public class CustomPin : Pin
{
    public static readonly BindableProperty ImageSourceProperty =
        BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(CustomPin));

    // 新增顶部文本属性
    public static readonly BindableProperty TopTextProperty =
        BindableProperty.Create(nameof(TopText), typeof(string), typeof(CustomPin));

    public ImageSource? ImageSource
    {
        get => (ImageSource?)GetValue(ImageSourceProperty);
        set => SetValue(ImageSourceProperty, value);
    }

    public string? TopText
    {
        get => (string?)GetValue(TopTextProperty);
        set => SetValue(TopTextProperty, value);
    }
}

2. 添加文本绘制与图片合成方法

在CustomMapHandler.cs中新增方法,将文本绘制到原Pin图标的上方,合成新的Bitmap:

private Bitmap DrawTextOnTopOfPin(Bitmap originalBitmap, string text)
{
    // 可根据需求调整字体、间距、颜色等参数
    int textSize = 40;
    int padding = 8;
    // 计算合成后图片的宽高:取文本宽度和原图宽度的最大值,高度为文本+间距+原图高度
    int totalWidth = Math.Max(originalBitmap.Width, (int)new Paint().MeasureText(text) + 10);
    int totalHeight = originalBitmap.Height + textSize + padding;

    // 创建空白Bitmap用于绘制
    Bitmap combinedBitmap = Bitmap.CreateBitmap(totalWidth, totalHeight, Bitmap.Config.Argb8888);
    Canvas canvas = new Canvas(combinedBitmap);

    // 配置文本画笔
    Paint textPaint = new Paint();
    textPaint.Color = Android.Graphics.Color.Black;
    textPaint.TextSize = textSize;
    textPaint.TextAlign = Paint.Align.Center;
    textPaint.FakeBoldText = true;

    // 绘制文本到顶部居中位置
    float textX = totalWidth / 2f;
    float textY = textSize - textPaint.Descent();
    canvas.DrawText(text, textX, textY, textPaint);

    // 绘制原图标到文本下方居中位置
    float iconX = (totalWidth - originalBitmap.Width) / 2f;
    float iconY = textSize + padding;
    canvas.DrawBitmap(originalBitmap, iconX, iconY, null);

    return combinedBitmap;
}

3. 修改Pin加载逻辑,应用合成图片

修改CustomMapHandler.cs中AddPins方法里的图片加载逻辑,当存在顶部文本时使用合成后的图片:

cp.ImageSource.LoadImage(MauiContext, result =>
{
    if (result?.Value is BitmapDrawable bitmapDrawable)
    {
        Bitmap finalBitmap = bitmapDrawable.Bitmap;
        // 如果设置了顶部文本,合成新图片
        if (!string.IsNullOrEmpty(cp.TopText))
        {
            finalBitmap = DrawTextOnTopOfPin(finalBitmap, cp.TopText);
        }
        markerOption.SetIcon(BitmapDescriptorFactory.FromBitmap(finalBitmap));
    }

    AddMarker(Map, pin, Markers, markerOption);
});

4. 设置Pin的顶部文本

在MainPage.xaml.cs的insertOnMap方法中,给CustomPin添加TopText值:

var customPinFromResource = new CustomPin()
{
    Label = "From Resource",
    TopText = "123", // 设置要显示在Pin上方的文本
    Location = new Location(48.95049, 2.62891),
    Address = "Address3",
    ImageSource = ImageSource.FromFile("map_large_vertical_stopped_marker.png"),
    Map = map
};

内容的提问来源于stack exchange,提问作者Frederico Nicola

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 03:27:02