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

如何在新版Android设备上用Xamarin.Forms自定义相机拍摄高分辨率照片

问题:Xamarin.Forms CameraX拍照分辨率受限(多摄像头Android设备)

我基于netstandard2.0框架开发Xamarin.Forms跨平台应用,部署目标为Android和iOS双平台,核心需求是实现自定义相机,捕获后置摄像头的高分辨率照片并发送至电脑分析。

iOS端已实现全分辨率拍照,但Android端在不同设备上表现不一致:

  • 三星SM-A510F(Android 7.0 API 24):可拍摄12MP照片,与原生相机效果一致
  • 三星SM-A415F(Android 10.0 API 29,配备3个后置摄像头:5MP、8MP、48MP):拍照分辨率被限制为3.5MP(2560*1440)

我通过自定义CameraPreview视图并绑定CameraPreviewRenderer实现跨平台视图关联,相关代码如下:

共享代码:CameraPreview

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace XamPOC.CustomRenderer
{
    public enum CameraOptions
    {
        Rear,
        Front
    }
    public class CameraPreview : View
    {
        public static readonly BindableProperty CameraProperty = BindableProperty.Create(propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraPreview), defaultValue: CameraOptions.Rear);
        public CameraOptions Camera
        {
            get
            {
                return (CameraOptions)GetValue(CameraProperty);
            }
            set
            {
                SetValue(CameraProperty, value);
            }
        }

        public void CapturePhoto()
        {
            MessagingCenter.Send(this, "CapturePhoto");
        }
    }
}

Android平台:CameraPreviewRenderer

using Android.Content;
using Android.Hardware.Camera2;
using Android.Util;
using AndroidX.Camera.Core;
using AndroidX.Camera.Lifecycle;
using AndroidX.Core.Content;
using Java.Util.Concurrent;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using XamPOC;
using XamPOC.Droid;
using XamPOC.CustomRenderer;
using System;
using Android.App;
using Android.Graphics;
using Android.Hardware.Camera2.Params;
using AndroidX.Camera.Camera2.InterOp;
using AndroidX.Lifecycle;
using Java.Lang;
using XamPOC.Services;
using XamPOC.CustomRenderer.Droid;
using AndroidX.Camera.Core.Impl;
using AndroidX.Camera.Camera2.Internal.Compat;
using Android.Net.Wifi.Aware;

[assembly: ExportRenderer(typeof(CameraPreview), typeof(CameraPreviewRenderer))]
namespace XamPOC.Droid
{
    public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview>
    {
        private UICameraPreview _uiCameraPreview;
        private IExecutorService _cameraExecutor;
        private ImageCapture _imageCapture;
        private CameraManager _cameraManager;

        public CameraPreviewRenderer(Context context) : base(context)
        {
            _cameraManager = (CameraManager) Context.GetSystemService(Context.CameraService);
            _cameraManager.GetCameraIdList();
        }

        protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null)
            {
                MessagingCenter.Unsubscribe<CameraPreview>(this, "CapturePhoto");
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    _uiCameraPreview = new UICameraPreview(Context);
                    SetNativeControl(_uiCameraPreview);

                    _cameraExecutor = Executors.NewSingleThreadExecutor();
                    StartCamera();
                }
                MessagingCenter.Subscribe<CameraPreview>(this, "CapturePhoto", async (sender) => TakePhoto());
            }
        }

        private void StartCamera()
        {
            var cameraProviderFuture = ProcessCameraProvider.GetInstance(Context);
            cameraProviderFuture.AddListener(new Runnable(() =>
            {
                var cameraProvider = (ProcessCameraProvider)cameraProviderFuture.Get();

                var preview = new Preview.Builder().Build();
                preview.SetSurfaceProvider(_uiCameraPreview.previewView.SurfaceProvider);
                var cameraSelector = new CameraSelector.Builder()
                    .RequireLensFacing(CameraSelector.LensFacingBack)
                    .Build();

                _imageCapture = new ImageCapture.Builder()
                    .SetCaptureMode(ImageCapture.CaptureModeMaximizeQuality)
                    .Build();

                try
                {
                    cameraProvider.UnbindAll();
                    var list = cameraProvider.AvailableCameraInfos;
                    foreach (var cameraInfo in list)
                    {
                        var c = cameraInfo;
                    }
                    var camera = cameraProvider.BindToLifecycle(GetLifecycleOwner(), cameraSelector, preview, _imageCapture);
                }
                catch (System.Exception ex)
                {
                    Log.Error("CameraPreviewRenderer", "Use case binding failed", ex);
                }
            }), ContextCompat.GetMainExecutor(Context));
        }

        private ILifecycleOwner GetLifecycleOwner()
        {
            if (Context is ILifecycleOwner lifecycleOwner)
            {
                return lifecycleOwner;
            }

            if (Context is Activity activity)
            {
                var lifecycleRegistry = new LifecycleRegistry((ILifecycleOwner)activity);
                lifecycleRegistry.SetCurrentState(Lifecycle.State.Started);
                return (ILifecycleOwner)lifecycleRegistry;
            }

            throw new InvalidOperationException("Context does not implement ILifecycleOwner");
        }

        public void TakePhoto()
        {
            if (_imageCapture == null)
                return;
            var fileService = DependencyService.Get<IFileService>();
            var folder = fileService.GetPicturesFolder();
            var photoFile = new Java.IO.File(System.IO.Path.Combine(folder, $"CCube{DateTime.Now:yyyyMMdd_HHmmss}.jpg"));
            var outputOptions = new ImageCapture.OutputFileOptions.Builder(photoFile)
                .Build();

            _imageCapture.TakePicture(outputOptions, _cameraExecutor, new ImageCaptureCallback(photoFile));
        }

        private class ImageCaptureCallback : Java.Lang.Object, ImageCapture.IOnImageSavedCallback
        {
            private Java.IO.File _photoFile;

            public ImageCaptureCallback(Java.IO.File photoFile)
            {
                _photoFile = photoFile;
            }

            public void OnError(ImageCaptureException exception)
            {
                Log.Error("CameraPreviewRenderer", "Photo capture failed: " + exception.Message);
            }

            public void OnImageSaved(ImageCapture.OutputFileResults outputFileResults)
            {
                Log.Debug("CameraPreviewRenderer", "Photo capture succeeded: " + _photoFile.AbsolutePath);
                byte[] ImageBytes = System.IO.File.ReadAllBytes(_photoFile.AbsolutePath);
                MessagingCenter.Send(this, "PhotoArray", ImageBytes);
                
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                _cameraExecutor?.Shutdown();
            }
        }
    }
}

CameraPage布局代码

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XamPOC.Views.CameraPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:XamPOC.CustomRenderer"
    xmlns:vm="clr-namespace:XamPOC.ViewModels"
    Title="CameraPage"
    x:DataType="vm:CameraViewModel"
    NavigationPage.HasNavigationBar="true">
    <Grid Padding="0" BackgroundColor="Black">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition x:Name="ImageRow" Height="10*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <local:CameraPreview
            x:Name="ImageView"
            Grid.Row="1"
            Grid.RowSpan="11"
            Grid.Column="0"
            Grid.ColumnSpan="12"
            BindingContext="{Binding CameraPreview}" />
        <ImageButton
            Grid.Row="8"
            Grid.RowSpan="3"
            Grid.Column="4"
            Grid.ColumnSpan="4"
            Margin="10"
            BackgroundColor="Transparent"
            Clicked="Button_Clicked"
            Source="Camera.png" />
        <FlexLayout
            Grid.ColumnSpan="12"
            AlignContent="Center"
            AlignItems="Center"
            Direction="Row"
            JustifyContent="SpaceEvenly">
            <Label
                HorizontalTextAlignment="Center"
                Text="CLI"
                TextColor="White"
                WidthRequest="85" />
            <Switch Toggled="Switch_Toggled" />
            <Label
                HorizontalTextAlignment="Center"
                Text="DERM"
                TextColor="White"
                WidthRequest="85" />
        </FlexLayout>
    </Grid>
</ContentPage>

我也尝试过使用CameraXBasics仓库,遇到了同样的分辨率不足问题,想知道这是否是Xamarin.Forms自定义相机的限制?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:37:32