.NET MAUI中实现CameraPreview上方带透明圆形的遮罩视图
.NET MAUI 适配带透明圆形的相机遮罩组件
针对你从Xamarin.Forms升级到.NET MAUI时,无法适配原NativeOverlayView的问题,下面是完整的MAUI适配方案,采用MAUI官方推荐的Handler模式实现:
1. 共享项目定义抽象视图
先在共享项目中创建跨平台的OverlayView,定义可绑定属性以便在XAML中配置:
public class OverlayView : View { public static readonly BindableProperty ShowOverlayProperty = BindableProperty.Create(nameof(ShowOverlay), typeof(bool), typeof(OverlayView), false, propertyChanged: OnOverlayPropertyChanged); public static readonly BindableProperty OpacityProperty = BindableProperty.Create(nameof(Opacity), typeof(float), typeof(OverlayView), 0.5f, propertyChanged: OnOverlayPropertyChanged); public static readonly BindableProperty OverlayBackgroundColorProperty = BindableProperty.Create(nameof(OverlayBackgroundColor), typeof(Color), typeof(OverlayView), Colors.Gray, propertyChanged: OnOverlayPropertyChanged); public static readonly BindableProperty ShapeProperty = BindableProperty.Create(nameof(Shape), typeof(OverlayShape), typeof(OverlayView), OverlayShape.Circle, propertyChanged: OnOverlayPropertyChanged); public bool ShowOverlay { get => (bool)GetValue(ShowOverlayProperty); set => SetValue(ShowOverlayProperty, value); } public float Opacity { get => (float)GetValue(OpacityProperty); set => SetValue(OpacityProperty, value); } public Color OverlayBackgroundColor { get => (Color)GetValue(OverlayBackgroundColorProperty); set => SetValue(OverlayBackgroundColorProperty, value); } public OverlayShape Shape { get => (OverlayShape)GetValue(ShapeProperty); set => SetValue(ShapeProperty, value); } private static void OnOverlayPropertyChanged(BindableObject bindable, object oldValue, object newValue) { if (bindable is OverlayView overlayView) { overlayView.Redraw(); } } public void Redraw() { Handler?.Invoke(nameof(Redraw)); } public enum OverlayShape { Circle, CustomPath } }
2. Android平台实现原生视图与Handler
MAUI中不再使用Renderer,改用Handler关联跨平台视图和原生控件:
2.1 Android原生OverlayView
保留原Xamarin的绘图逻辑,调整构造函数和类型适配:
using Android.Content; using Android.Graphics; using Android.Views; namespace YourAppNamespace.Platforms.Android; public class NativeOverlayView : View { private Bitmap? _windowFrame; private float _overlayOpacity = 0.5f; private bool _showOverlay = false; private Color _overlayColor = Android.Graphics.Color.Gray; private OverlayShape _overlayShape = OverlayShape.Circle; public bool ShowOverlay { get => _showOverlay; set { bool repaint = !_showOverlay; _showOverlay = value; if (repaint) { Redraw(); } } } public float Opacity { get => _overlayOpacity; set { _overlayOpacity = value; Redraw(); } } public Android.Graphics.Color OverlayBackgroundColor { get => _overlayColor; set { _overlayColor = value; Redraw(); } } public OverlayShape Shape { get => _overlayShape; set { _overlayShape = value; Redraw(); } } public NativeOverlayView(Context context) : base(context) { SetWillNotDraw(false); } protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); if (ShowOverlay) { _windowFrame ??= CreateWindowFrame(); canvas.DrawBitmap(_windowFrame, 0, 0, null); } } private void Redraw() { if (ShowOverlay) { _windowFrame?.Recycle(); _windowFrame = null; Invalidate(); } } private Bitmap CreateWindowFrame() { float width = Width; float height = Height; var bitmap = Bitmap.CreateBitmap((int)width, (int)height, Bitmap.Config.Argb8888); var osCanvas = new Canvas(bitmap); var paint = new Paint(PaintFlags.AntiAlias) { Color = OverlayBackgroundColor, Alpha = (int)(255 * Opacity) }; var outerRectangle = new RectF(0, 0, width, height); osCanvas.DrawRect(outerRectangle, paint); paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear)); switch (Shape) { case OverlayShape.Circle: float radius = Math.Min(width, height) * 0.45f; osCanvas.DrawCircle(width / 2, height / 2, radius, paint); break; case OverlayShape.CustomPath: var path = new Path(); path.MoveTo(width / 2, height / 5); path.CubicTo(5 * width / 14, 0, 0, height / 15, width / 28, 2 * height / 5); path.CubicTo(width / 14, 2 * height / 3, 3 * width / 7, 5 * height / 6, width / 2, height); path.CubicTo(4 * width / 7, 5 * height / 6, 13 * width / 14, 2 * height / 3, 27 * width / 28, 2 * height / 5); path.CubicTo(width, height / 15, 9 * width / 14, 0, width / 2, height / 5); osCanvas.DrawPath(path, paint); break; } return bitmap; } protected override void OnLayout(bool changed, int l, int t, int r, int b) { base.OnLayout(changed, l, t, r, b); _windowFrame?.Recycle(); _windowFrame = null; } public enum OverlayShape { Circle, CustomPath } }
2.2 实现OverlayViewHandler
编写Handler绑定跨平台属性到原生控件:
using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; namespace YourAppNamespace.Platforms.Android; public class OverlayViewHandler : ViewHandler<OverlayView, NativeOverlayView> { protected override NativeOverlayView CreatePlatformView() { return new NativeOverlayView(Context); } protected override void ConnectHandler(NativeOverlayView platformView) { base.ConnectHandler(platformView); UpdateShowOverlay(); UpdateOpacity(); UpdateBackgroundColor(); UpdateShape(); } protected override void DisconnectHandler(NativeOverlayView platformView) { platformView.Dispose(); base.DisconnectHandler(platformView); } public static void MapShowOverlay(OverlayViewHandler handler, OverlayView view) { handler.UpdateShowOverlay(); } public static void MapOpacity(OverlayViewHandler handler, OverlayView view) { handler.UpdateOpacity(); } public static void MapOverlayBackgroundColor(OverlayViewHandler handler, OverlayView view) { handler.UpdateBackgroundColor(); } public static void MapShape(OverlayViewHandler handler, OverlayView view) { handler.UpdateShape(); } private void UpdateShowOverlay() { PlatformView.ShowOverlay = VirtualView.ShowOverlay; } private void UpdateOpacity() { PlatformView.Opacity = VirtualView.Opacity; } private void UpdateBackgroundColor() { PlatformView.OverlayBackgroundColor = VirtualView.OverlayBackgroundColor.ToPlatform(); } private void UpdateShape() { PlatformView.Shape = VirtualView.Shape switch { OverlayView.OverlayShape.Circle => NativeOverlayView.OverlayShape.Circle, OverlayView.OverlayShape.CustomPath => NativeOverlayView.OverlayShape.CustomPath, _ => NativeOverlayView.OverlayShape.Circle }; } }
3. 注册Handler
在MauiProgram.cs中注册Handler,让MAUI识别自定义视图:
builder.ConfigureMauiHandlers(handlers => { #if ANDROID handlers.AddHandler<OverlayView, OverlayViewHandler>(); #endif });
4. XAML中使用
将OverlayView叠加在CameraPreview上方,实现遮罩效果:
<Grid> <!-- 你的MAUI相机组件 --> <CameraView HorizontalOptions="Fill" VerticalOptions="Fill"/> <!-- 遮罩组件 --> <local:OverlayView ShowOverlay="True" Opacity="0.5" OverlayBackgroundColor="Gray" Shape="Circle" HorizontalOptions="Fill" VerticalOptions="Fill"/> </Grid>
关键适配说明
- MAUI弃用了Xamarin的Renderer模式,改用Handler实现跨平台视图与原生控件的绑定
- 处理了MAUI Color与Android Color的类型转换
- 保留了原绘图逻辑,确保遮罩效果一致
- 通过BindableProperty支持数据绑定,更符合MAUI开发规范
内容的提问来源于stack exchange,提问作者Alfredo Vidinhas
相关产品推荐
相关产品推荐

