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

如何用Handler替代已弃用的ButtonRenderer在按钮上绘制字符串?

改用MAUI Handler替代弃用的ButtonRenderer实现按钮额外文本绘制

问题背景

之前通过自定义ButtonRenderer在按钮文本外额外绘制快捷键文本,代码如下。由于Renderer已被弃用,需要改用MAUI的Handler机制重新实现该功能:

public class HotkeyButtonRenderer : ButtonRenderer
{
    public HotkeyButtonRenderer(Context ctx) : base(ctx)
    {

    }

    protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        base.OnElementChanged(e);
        SetWillNotDraw(false);
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if (Element is not null && (
            e.PropertyName == HotkeyButton.HotkeyTextProperty.PropertyName ||
            e.PropertyName == HotkeyButton.BorderColorProperty.PropertyName))
        {
            Invalidate();
        }
    }

    public override void Draw(Canvas canvas)
    {
        base.Draw(canvas);
        if (Element is HotkeyButton element && !string.IsNullOrEmpty(element.HotkeyText))
        {
            string textToDraw = $"({element.HotkeyText})";
            float textSize = Control.TextSize / 1.5f;
            Paint paint = new Paint();
            Rect textBounds = new Rect();
            paint.TextSize = textSize;
            paint.GetTextBounds(textToDraw,0,textToDraw.Length, textBounds);
            int textHeight = textBounds.Height();
            float x = 5;
            float y = Height- textHeight / 2;

            canvas.DrawText(textToDraw,
                x,
                y,
                new TextPaint
                {
                    Color = element.BorderColor.ToAndroid(),
                    TextSize = textSize
                }
            );
        }
    }
}

解决方案

1. 确认自定义Button控件(若已存在可跳过)

确保HotkeyButton自定义控件包含所需的绑定属性:

public class HotkeyButton : Button
{
    public static readonly BindableProperty HotkeyTextProperty =
        BindableProperty.Create(nameof(HotkeyText), typeof(string), typeof(HotkeyButton), string.Empty);

    public static readonly BindableProperty BorderColorProperty =
        BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(HotkeyButton), Colors.Black);

    public string HotkeyText
    {
        get => (string)GetValue(HotkeyTextProperty);
        set => SetValue(HotkeyTextProperty, value);
    }

    public Color BorderColor
    {
        get => (Color)GetValue(BorderColorProperty);
        set => SetValue(BorderColorProperty, value);
    }
}

2. 实现Android平台的HotkeyButtonHandler

在Android项目中创建Handler,通过自定义Drawable处理绘制逻辑:

using Android.Content;
using Android.Graphics;
using Android.Widget;
using Microsoft.Maui.Handlers;
using YourNamespace; // 替换为你的实际命名空间

public class HotkeyButtonHandler : ButtonHandler
{
    private CustomButtonDrawable? _customDrawable;

    public HotkeyButtonHandler() : base(PropertyMapper, CommandMapper)
    {
    }

    public HotkeyButtonHandler(IPropertyMapper<Button, IButtonHandler>? mapper = null, CommandMapper? commandMapper = null) 
        : base(mapper ?? PropertyMapper, commandMapper ?? CommandMapper)
    {
    }

    protected override void ConnectHandler(Android.Widget.Button platformView)
    {
        base.ConnectHandler(platformView);
        // 创建自定义Drawable并绑定到原生按钮
        _customDrawable = new CustomButtonDrawable(this);
        platformView.SetBackgroundDrawable(_customDrawable);
    }

    protected override void DisconnectHandler(Android.Widget.Button platformView)
    {
        // 释放资源
        _customDrawable?.Dispose();
        _customDrawable = null;
        base.DisconnectHandler(platformView);
    }

    public override void UpdateValue(string propertyName)
    {
        base.UpdateValue(propertyName);
        // 监听自定义属性变化,触发重绘
        if (propertyName == HotkeyButton.HotkeyTextProperty.PropertyName ||
            propertyName == HotkeyButton.BorderColorProperty.PropertyName)
        {
            _customDrawable?.InvalidateSelf();
        }
    }

    // 自定义Drawable负责绘制额外的快捷键文本
    private class CustomButtonDrawable : Drawable
    {
        private readonly HotkeyButtonHandler _handler;
        private readonly TextPaint _textPaint = new TextPaint(PaintFlags.AntiAlias);

        public CustomButtonDrawable(HotkeyButtonHandler handler)
        {
            _handler = handler;
        }

        public override void Draw(Canvas canvas)
        {
            // 先绘制原生按钮背景
            _handler.PlatformView?.Background?.Draw(canvas);

            var element = _handler.VirtualView as HotkeyButton;
            if (element == null || string.IsNullOrEmpty(element.HotkeyText))
                return;

            string textToDraw = $"({element.HotkeyText})";
            float textSize = _handler.PlatformView.TextSize / 1.5f;
            
            _textPaint.TextSize = textSize;
            _textPaint.Color = element.BorderColor.ToAndroid();

            // 计算文本绘制位置:左下角偏移5,垂直居中于按钮底部区域
            Rect textBounds = new Rect();
            _textPaint.GetTextBounds(textToDraw, 0, textToDraw.Length, textBounds);
            float x = 5;
            float y = Bounds.Bottom - textBounds.Height() / 2;

            canvas.DrawText(textToDraw, x, y, _textPaint);
        }

        public override void SetAlpha(int alpha)
        {
            _textPaint.Alpha = alpha;
        }

        public override void SetColorFilter(ColorFilter? colorFilter)
        {
            _textPaint.SetColorFilter(colorFilter);
        }

        public override int Opacity => (int)Format.Translucent;
    }
}

3. 注册Handler

在项目的MauiProgram.cs中注册自定义Handler,让MAUI识别HotkeyButton对应的平台实现:

using Microsoft.Maui.Handlers;
using YourNamespace; // 替换为你的实际命名空间

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // 注册HotkeyButton的Handler
        builder.ConfigureMauiHandlers(handlers =>
        {
#if ANDROID
            handlers.AddHandler(typeof(HotkeyButton), typeof(HotkeyButtonHandler));
#endif
        });

        return builder.Build();
    }
}

关键说明

  • 用CustomButtonDrawable替代原Renderer的Draw方法,将绘制逻辑封装在Drawable中,符合MAUI Handler的设计模式
  • 在ConnectHandler中绑定Drawable,DisconnectHandler中释放资源,避免内存泄漏
  • 通过UpdateValue监听自定义属性变化,触发Drawable重绘,实现属性变更的实时响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:12:14