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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 21:45:54