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

