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

Xamarin.Forms平板端相机竖屏旋转适配问题求助

平板相机竖屏模式适配问题

我需要在平板设备上将相机从当前的横屏模式切换为竖屏模式,但修改SetTransform方法后始终无法成功——所有尝试都会导致旋转后的画面拉伸或仅显示屏幕一半大小。以下是我使用的相关代码:

public void ContinueOpenCamera()
{
    SetUpCamera();
    SetTransform();
    var manager = (CameraManager)Context.GetSystemService(Context.CameraService);
    manager.OpenCamera(cameraId, new CameraStateListener(this), null);
}

private void SetUpCamera()
{
    var manager = (CameraManager)Context.GetSystemService(Context.CameraService);
    var cameraList = manager.GetCameraIdList();
    try
    {
        for (var i = 0; i < cameraList.Length; ++i)
        {
            cameraId = cameraList[i];
            var characteristics = manager.GetCameraCharacteristics(cameraId);

            var facing = (Integer)characteristics.Get(CameraCharacteristics.LensFacing);

            if (facing == null || facing != Facing)
            {
                continue;
            }

            var map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap);
            if (map == null)
            {
                continue;
            }

            sensorOrientation = (int)characteristics.Get(CameraCharacteristics.SensorOrientation);
            textureViewSize = !Config.Rotated
                ? GetBestSize(height, width, map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture))))
                : GetBestSize(width, height, map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture))));

            if (sensorOrientation == 90 || sensorOrientation == 270)
            {
                textureViewSize = new Size(textureViewSize.Height, textureViewSize.Width);
            }

            paint.StrokeWidth = System.Math.Min(width, height) / 300;
            greenTextPaint.TextSize = blueTextPaint.TextSize = paint.StrokeWidth * 15;
            return;
        }
    }
    catch (CameraAccessException e)
    {
        e.PrintStackTrace();
    }
}

private Size GetBestSize(int width, int height, IEnumerable<Size> sizes)
{
    var dw = (double)width;
    var dh = (double)height;
    var targetAspect = dw / dh;
    var sorted = sizes.Select(size => new Tuple<double, Size>(
        System.Math.Max(
            System.Math.Abs(dw / size.Width - 1),
            System.Math.Abs(dh / size.Height - 1)
        ), size))
        .OrderBy(value => value.Item1).ToArray();
    var best = sorted[0];
    var bestAspect = System.Math.Abs(best.Item2.Width / best.Item2.Height - targetAspect);
    for (int i = 1; i < sorted.Length; ++i)
    {
        var current = sorted[i];
        if (System.Math.Abs(current.Item1 - best.Item1) > 1e-10)
            break;
        var aspect = System.Math.Abs(current.Item2.Width / current.Item2.Height - targetAspect);
        if (bestAspect - aspect > 1e-10)
        {
            best = current;
            bestAspect = aspect;
        }
    }
    return best.Item2;
}

private void SetTransform()
{
    var matrix = new Matrix();
    var viewRect = new RectF(0, 0, width, height);
    var scale = System.Math.Max(
        (float)width / textureViewSize.Width,
        (float)height / textureViewSize.Height);
    var bufferRect = new RectF(0, 0,
        textureViewSize.Width * scale, textureViewSize.Height * scale);

    bitmapScale = System.Math.Max(
           System.Math.Max(
               bufferRect.Width() / MaxSize,
               bufferRect.Height() / MaxSize
               ),
           1
           );
    bitmapSize = new Size(
            Convert.ToInt32(bufferRect.Width() / bitmapScale),
            Convert.ToInt32(bufferRect.Height() / bitmapScale));

    pixels = new int[bitmapSize.Width * bitmapSize.Height];
    bytes = new byte[pixels.Length * 4];

    bitmap = Bitmap.CreateBitmap(bitmapSize.Width, bitmapSize.Height, Bitmap.Config.Argb8888);

    matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Fill);

    textureView.SetTransform(matrix);
    viewRect.Dispose();
    bufferRect.Dispose();
}

问题分析与修复方案

核心问题在于相机传感器方向与竖屏显示的适配逻辑缺失,以及SetTransform中未处理旋转后的尺寸映射,导致画面拉伸或裁切。

1. 修正SetUpCamera的尺寸选择逻辑

原代码依赖Config.Rotated判断尺寸选择,未直接匹配当前屏幕竖屏状态,调整后直接根据屏幕宽高判断竖屏,确保尺寸选择正确:

private void SetUpCamera()
{
    var manager = (CameraManager)Context.GetSystemService(Context.CameraService);
    var cameraList = manager.GetCameraIdList();
    try
    {
        for (var i = 0; i < cameraList.Length; ++i)
        {
            cameraId = cameraList[i];
            var characteristics = manager.GetCameraCharacteristics(cameraId);

            var facing = (Integer)characteristics.Get(CameraCharacteristics.LensFacing);

            if (facing == null || facing != Facing)
            {
                continue;
            }

            var map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap);
            if (map == null)
            {
                continue;
            }

            sensorOrientation = (int)characteristics.Get(CameraCharacteristics.SensorOrientation);
            // 根据屏幕宽高判断竖屏状态,交换宽高传入GetBestSize
            bool isPortrait = height > width;
            textureViewSize = GetBestSize(
                isPortrait ? height : width,
                isPortrait ? width : height,
                map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture)))
            );

            if (sensorOrientation == 90 || sensorOrientation == 270)
            {
                textureViewSize = new Size(textureViewSize.Height, textureViewSize.Width);
            }

            paint.StrokeWidth = System.Math.Min(width, height) / 300;
            greenTextPaint.TextSize = blueTextPaint.TextSize = paint.StrokeWidth * 15;
            return;
        }
    }
    catch (CameraAccessException e)
    {
        e.PrintStackTrace();
    }
}

2. 重写SetTransform方法,添加旋转与比例适配

原方法使用Matrix.ScaleToFit.Fill强制拉伸画面,且未处理传感器旋转。修改后保持画面比例,添加旋转逻辑并居中显示:

private void SetTransform()
{
    var matrix = new Matrix();
    var viewRect = new RectF(0, 0, width, height);
    float scale;

    // 根据传感器方向调整缩放比例计算方式
    if (sensorOrientation == 90 || sensorOrientation == 270)
    {
        // 传感器横置,竖屏显示时交换宽高计算缩放
        scale = System.Math.Max(
            (float)width / textureViewSize.Height,
            (float)height / textureViewSize.Width
        );
    }
    else
    {
        scale = System.Math.Max(
            (float)width / textureViewSize.Width,
            (float)height / textureViewSize.Height
        );
    }

    // 计算缓冲区域尺寸并居中
    float bufferWidth = textureViewSize.Width * scale;
    float bufferHeight = textureViewSize.Height * scale;
    var bufferRect = new RectF(0, 0, bufferWidth, bufferHeight);
    bufferRect.Offset((viewRect.Width() - bufferWidth) / 2, (viewRect.Height() - bufferHeight) / 2);

    // 先适配尺寸,再根据传感器方向旋转画面
    matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Center);
    matrix.PostRotate(sensorOrientation, viewRect.CenterX(), viewRect.CenterY());

    // 同步更新bitmap相关尺寸
    bitmapScale = System.Math.Max(
           System.Math.Max(bufferWidth / MaxSize, bufferHeight / MaxSize),
           1f
           );
    bitmapSize = new Size(
            Convert.ToInt32(bufferWidth / bitmapScale),
            Convert.ToInt32(bufferHeight / bitmapScale));

    pixels = new int[bitmapSize.Width * bitmapSize.Height];
    bytes = new byte[pixels.Length * 4];

    bitmap = Bitmap.CreateBitmap(bitmapSize.Width, bitmapSize.Height, Bitmap.Config.Argb8888);

    textureView.SetTransform(matrix);
    viewRect.Dispose();
    bufferRect.Dispose();
}

3. 关键逻辑说明

  • 传感器方向适配:平板相机传感器多为横置(90/270度),竖屏显示时需旋转画面,同时交换宽高计算缩放比例,避免拉伸。
  • 比例保持:使用Matrix.ScaleToFit.Center替代Fill,确保画面比例正确,通过偏移让画面居中显示。
  • 旋转矩阵:PostRotate根据传感器方向旋转画面,匹配竖屏视觉需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:02:21