第08章 - 小部件系统

第08章:小部件(Widget)系统

8.1 小部件概述

8.1.1 什么是小部件

小部件(Widget)是叠加在地图上的 UI 元素,用于提供导航控制、信息显示等功能。它们独立于地图内容,始终显示在最上层。

┌─────────────────────────────────────────────────────────────────┐
│                        地图视图                                  │
│  ┌───┐                                              ┌───────┐  │
│  │ + │  缩放按钮                                     │ 比例尺 │  │
│  │ - │                                              └───────┘  │
│  └───┘                                                         │
│                                                                 │
│                        地图内容                                 │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐  │
│  │                     日志信息区域                          │  │
│  └─────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

8.1.2 IWidget 接口

public interface IWidget
{
    // 标识
    string Name { get; }
    
    // 位置和布局
    HorizontalAlignment HorizontalAlignment { get; set; }
    VerticalAlignment VerticalAlignment { get; set; }
    float MarginX { get; set; }
    float MarginY { get; set; }
    
    // 状态
    bool Enabled { get; set; }
    
    // 交互
    bool OnPointerPressed(Navigator navigator, WidgetEventArgs e);
    bool OnPointerMoved(Navigator navigator, WidgetEventArgs e);
    bool OnPointerReleased(Navigator navigator, WidgetEventArgs e);
    bool OnTapped(Navigator navigator, WidgetEventArgs e);
}

8.1.3 内置小部件类型

小部件 功能
ZoomInOutWidget 缩放控制按钮
ScaleBarWidget 比例尺显示
LoggingWidget 日志显示
PerformanceWidget 性能信息显示
MouseCoordinatesWidget 鼠标坐标显示
HyperlinkWidget 超链接
ButtonWidget 可点击按钮
TextBoxWidget 文本框

8.2 ZoomInOutWidget - 缩放控制

8.2.1 基本使用

var zoomWidget = new ZoomInOutWidget
{
    // 位置
    HorizontalAlignment = HorizontalAlignment.Right,
    VerticalAlignment = VerticalAlignment.Top,
    MarginX = 20,
    MarginY = 20,
    
    // 布局
    Orientation = Orientation.Vertical,  // 垂直排列
    
    // 样式
    BackColor = Color.White,
    Opacity = 0.9f
};

map.Widgets.Add(zoomWidget);

8.2.2 自定义缩放按钮

var customZoomWidget = new ZoomInOutWidget
{
    // 使用自定义图标
    PlusText = "+",
    MinusText = "-",
    
    // 大小
    Size = 40,
    
    // 边距
    MarginX = 15,
    MarginY = 100,
    
    // 样式
    BackColor = new Color(50, 50, 50),
    TextColor = Color.White
};

8.3 ScaleBarWidget - 比例尺

8.3.1 基本使用

var scaleBarWidget = new ScaleBarWidget(map)
{
    // 位置
    HorizontalAlignment = HorizontalAlignment.Left,
    VerticalAlignment = VerticalAlignment.Bottom,
    MarginX = 20,
    MarginY = 20,
    
    // 文本对齐
    TextAlignment = Alignment.Center,
    
    // 颜色
    TextColor = Color.Black,
    Halo = Color.White,
    
    // 最大宽度
    MaxWidth = 150
};

map.Widgets.Add(scaleBarWidget);

8.3.2 配置比例尺样式

var styledScaleBar = new ScaleBarWidget(map)
{
    // 单位制
    UnitConverter = MetricUnitConverter.Instance,  // 公制单位
    // 或
    // UnitConverter = ImperialUnitConverter.Instance,  // 英制单位
    
    // 样式
    ScaleBarMode = ScaleBarMode.Single,  // 单条或双条
    TickLength = 5,
    
    // 颜色
    TextColor = Color.Black,
    Halo = Color.White,
    StrokeWidth = 2,
    StrokeWidthHalo = 4
};

8.4 LoggingWidget - 日志显示

8.4.1 配置日志小部件

// LoggingWidget 默认已添加到 Map
// 可以配置其显示行为

// 控制是否显示日志(全局设置)
LoggingWidget.ShowLoggingInMap = ActiveMode.Yes;       // 始终显示
LoggingWidget.ShowLoggingInMap = ActiveMode.No;        // 从不显示
LoggingWidget.ShowLoggingInMap = ActiveMode.OnlyInDebugMode;  // 仅调试时显示(默认)

8.4.2 自定义日志处理

// 订阅 Mapsui 日志事件
Mapsui.Logging.Logger.LogDelegate += (level, message, ex) =>
{
    var logMessage = $"[{level}] {message}";
    if (ex != null)
    {
        logMessage += $" - {ex.Message}";
    }
    
    // 输出到控制台
    Console.WriteLine(logMessage);
    
    // 或转发到您的日志框架
    // _logger.Log(ConvertLevel(level), ex, message);
};

8.5 PerformanceWidget - 性能信息

8.5.1 显示性能信息

var performanceWidget = new PerformanceWidget
{
    // 位置
    HorizontalAlignment = HorizontalAlignment.Left,
    VerticalAlignment = VerticalAlignment.Top,
    MarginX = 10,
    MarginY = 10,
    
    // 样式
    BackColor = new Color(0, 0, 0, 180),
    TextColor = Color.White
};

map.Widgets.Add(performanceWidget);

8.5.2 性能监控

// PerformanceWidget 显示的信息包括:
// - FPS(每秒帧数)
// - 渲染时间
// - 要素数量
// - 瓦片数量

// 点击性能小部件可以重置统计数据

8.6 MouseCoordinatesWidget - 坐标显示

8.6.1 显示鼠标坐标

var coordinatesWidget = new MouseCoordinatesWidget(map)
{
    // 位置
    HorizontalAlignment = HorizontalAlignment.Right,
    VerticalAlignment = VerticalAlignment.Bottom,
    MarginX = 10,
    MarginY = 10,
    
    // 样式
    BackColor = Color.White,
    TextColor = Color.Black
};

map.Widgets.Add(coordinatesWidget);

8.6.2 自定义坐标格式

// 创建自定义坐标格式化器
var formattedCoordinatesWidget = new MouseCoordinatesWidget(map)
{
    // 使用自定义格式化
    CoordinateFormatter = (x, y) =>
    {
        // 转换为经纬度
        var lonLat = SphericalMercator.ToLonLat(x, y);
        return $"经度: {lonLat.X:F6}° 纬度: {lonLat.Y:F6}°";
    }
};

8.7 ButtonWidget - 按钮小部件

8.7.1 创建自定义按钮

var buttonWidget = new ButtonWidget
{
    // 文本
    Text = "定位",
    
    // 位置
    HorizontalAlignment = HorizontalAlignment.Right,
    VerticalAlignment = VerticalAlignment.Top,
    MarginX = 20,
    MarginY = 80,
    
    // 大小
    Width = 60,
    Height = 30,
    
    // 样式
    BackColor = Color.White,
    TextColor = Color.Black
};

// 点击事件
buttonWidget.Tapped += (sender, args) =>
{
    // 处理点击
    map.Navigator.CenterOn(116.4, 39.9);
    args.Handled = true;
};

map.Widgets.Add(buttonWidget);

8.7.2 带图标的按钮

var iconButtonWidget = new ButtonWidget
{
    // 使用图像
    ImageSource = "embedded://MyApp.Resources.locate.png",
    
    // 位置和大小
    HorizontalAlignment = HorizontalAlignment.Right,
    VerticalAlignment = VerticalAlignment.Top,
    MarginX = 20,
    MarginY = 80,
    Width = 40,
    Height = 40,
    
    // 圆角
    CornerRadius = 20,
    
    // 样式
    BackColor = Color.White
};

iconButtonWidget.Tapped += (sender, args) =>
{
    // 定位到当前位置
    LocateCurrentPosition();
    args.Handled = true;
};

8.8 HyperlinkWidget - 超链接

8.8.1 添加超链接

var hyperlinkWidget = new HyperlinkWidget
{
    // 文本和链接
    Text = "© OpenStreetMap",
    Url = "https://www.openstreetmap.org/copyright",
    
    // 位置
    HorizontalAlignment = HorizontalAlignment.Right,
    VerticalAlignment = VerticalAlignment.Bottom,
    MarginX = 10,
    MarginY = 10,
    
    // 样式
    TextColor = Color.Blue,
    BackColor = new Color(255, 255, 255, 200)
};

map.Widgets.Add(hyperlinkWidget);

8.9 TextBoxWidget - 文本框

8.9.1 显示信息文本

var textBoxWidget = new TextBoxWidget
{
    // 位置
    HorizontalAlignment = HorizontalAlignment.Center,
    VerticalAlignment = VerticalAlignment.Top,
    MarginY = 10,
    
    // 文本
    Text = "欢迎使用 Mapsui 地图",
    
    // 样式
    BackColor = new Color(0, 0, 0, 180),
    TextColor = Color.White,
    CornerRadius = 5,
    Padding = 10
};

map.Widgets.Add(textBoxWidget);

8.9.2 动态更新文本

public class InfoDisplayManager
{
    private readonly TextBoxWidget _infoWidget;
    
    public InfoDisplayManager(Map map)
    {
        _infoWidget = new TextBoxWidget
        {
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Bottom,
            MarginY = 50,
            BackColor = new Color(0, 0, 0, 200),
            TextColor = Color.White,
            Enabled = false  // 初始隐藏
        };
        
        map.Widgets.Add(_infoWidget);
    }
    
    public void ShowInfo(string message, int durationMs = 3000)
    {
        _infoWidget.Text = message;
        _infoWidget.Enabled = true;
        
        // 自动隐藏
        Task.Delay(durationMs).ContinueWith(_ =>
        {
            _infoWidget.Enabled = false;
        });
    }
}

8.10 自定义小部件

8.10.1 继承 BaseWidget

public class CompassWidget : BaseWidget
{
    private readonly Map _map;
    private double _rotation;
    
    public override string Name => "Compass";
    
    public float Size { get; set; } = 50;
    
    public CompassWidget(Map map)
    {
        _map = map;
        
        HorizontalAlignment = HorizontalAlignment.Right;
        VerticalAlignment = VerticalAlignment.Top;
        MarginX = 20;
        MarginY = 150;
    }
    
    public void UpdateRotation(double rotation)
    {
        _rotation = rotation;
    }
    
    public override bool OnTapped(Navigator navigator, WidgetEventArgs e)
    {
        // 点击时重置旋转
        navigator.RotateTo(0);
        return true;
    }
}

// 需要同时创建渲染器
public class CompassWidgetRenderer : IWidgetRenderer
{
    public void Draw(SKCanvas canvas, IWidget widget, float scale)
    {
        if (widget is CompassWidget compass)
        {
            var size = compass.Size * scale;
            var center = new SKPoint(size / 2, size / 2);
            
            // 绘制指南针背景
            using var bgPaint = new SKPaint
            {
                Color = SKColors.White,
                Style = SKPaintStyle.Fill,
                IsAntialias = true
            };
            canvas.DrawCircle(center, size / 2, bgPaint);
            
            // 绘制指针
            using var arrowPaint = new SKPaint
            {
                Color = SKColors.Red,
                Style = SKPaintStyle.Fill,
                IsAntialias = true
            };
            
            // 绘制北向指针
            var path = new SKPath();
            path.MoveTo(center.X, center.Y - size / 3);
            path.LineTo(center.X - size / 8, center.Y);
            path.LineTo(center.X + size / 8, center.Y);
            path.Close();
            
            canvas.Save();
            canvas.RotateDegrees((float)compass._rotation, center.X, center.Y);
            canvas.DrawPath(path, arrowPaint);
            canvas.Restore();
        }
    }
}

8.10.2 注册自定义渲染器

// 在应用启动时注册
WidgetRenderer.DefaultWidgetRenderers[typeof(CompassWidget)] = new CompassWidgetRenderer();

8.11 小部件布局管理

8.11.1 布局系统

// 小部件使用对齐和边距进行定位
// HorizontalAlignment: Left, Center, Right
// VerticalAlignment: Top, Center, Bottom
// MarginX, MarginY: 距离边缘的距离

// 左上角
widget.HorizontalAlignment = HorizontalAlignment.Left;
widget.VerticalAlignment = VerticalAlignment.Top;
widget.MarginX = 10;
widget.MarginY = 10;

// 右下角
widget.HorizontalAlignment = HorizontalAlignment.Right;
widget.VerticalAlignment = VerticalAlignment.Bottom;
widget.MarginX = 10;
widget.MarginY = 10;

// 中央底部
widget.HorizontalAlignment = HorizontalAlignment.Center;
widget.VerticalAlignment = VerticalAlignment.Bottom;
widget.MarginY = 20;

8.11.2 小部件组管理

public class WidgetManager
{
    private readonly Map _map;
    
    public WidgetManager(Map map)
    {
        _map = map;
    }
    
    public void SetupDefaultWidgets()
    {
        // 缩放控制 - 右上角
        _map.Widgets.Add(new ZoomInOutWidget
        {
            HorizontalAlignment = HorizontalAlignment.Right,
            VerticalAlignment = VerticalAlignment.Top,
            MarginX = 20,
            MarginY = 20
        });
        
        // 比例尺 - 左下角
        _map.Widgets.Add(new ScaleBarWidget(_map)
        {
            HorizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment = VerticalAlignment.Bottom,
            MarginX = 20,
            MarginY = 20
        });
        
        // 版权信息 - 右下角
        _map.Widgets.Add(new HyperlinkWidget
        {
            Text = "© OpenStreetMap",
            Url = "https://www.openstreetmap.org/copyright",
            HorizontalAlignment = HorizontalAlignment.Right,
            VerticalAlignment = VerticalAlignment.Bottom,
            MarginX = 10,
            MarginY = 10
        });
    }
    
    public void HideAllWidgets()
    {
        foreach (var widget in _map.Widgets)
        {
            widget.Enabled = false;
        }
    }
    
    public void ShowAllWidgets()
    {
        foreach (var widget in _map.Widgets)
        {
            widget.Enabled = true;
        }
    }
}

8.12 本章小结

本章详细介绍了 Mapsui 的小部件系统:

  1. 小部件概述:IWidget 接口和内置小部件类型
  2. ZoomInOutWidget:缩放控制按钮
  3. ScaleBarWidget:比例尺显示
  4. LoggingWidget:日志信息显示
  5. PerformanceWidget:性能监控
  6. MouseCoordinatesWidget:鼠标坐标显示
  7. ButtonWidget:自定义按钮
  8. HyperlinkWidget:超链接
  9. TextBoxWidget:文本显示
  10. 自定义小部件:实现自定义小部件和渲染器
  11. 布局管理:小部件定位和组织

在下一章中,我们将学习事件处理与用户交互。

8.13 思考与练习

  1. 创建一个图层切换小部件,允许用户切换底图。
  2. 实现一个显示当前缩放级别的小部件。
  3. 创建一个指南针小部件,随地图旋转而旋转。
  4. 实现一个简单的图例小部件。
  5. 设计一个工具栏小部件,包含多个功能按钮。
posted @ 2026-01-08 14:40  我才是银古  阅读(22)  评论(0)    收藏  举报