如何用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
相关产品推荐
相关产品推荐

