Xamarin.Form 可以将相机视图插入视图,例如 Instagram
Xamarin.Form It is possible insert camera view into view, like Instagram
是否可以使用 xamarin.form 创建一个界面来捕捉或选择图像,就像在 Instagram 上一样。
这将是一张像 Instagram 上那样的方形图片。我测试了 MediaPlugin,但它只是打开相机,我无法将它插入到视图中。
您应该创建一个自定义渲染器。
首先在您的共享项目中创建一个视图,如下所示:
public enum CameraOptions
{
Rear,
Front
}
public class CameraView : View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create(nameof(Camera), typeof(CameraOptions), typeof(CameraView), CameraOptions.Rear);
public CameraOptions Camera
{
get { return (CameraOptions)GetValue(CameraProperty); }
set { SetValue(CameraProperty, value); }
}
}
然后为每个平台创建一个渲染器。
Android:
[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewXamarinForms.Droid.Renderers
{
public class CameraViewRenderer : ViewRenderer<CameraView, NativeCameraView>
{
NativeCameraView cameraPreview;
public CameraViewRenderer(Context context) : base(context)
{
}
protected override async void OnElementChanged(ElementChangedEventArgs<CameraView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
cameraPreview = new NativeCameraView(Context);
SetNativeControl(cameraPreview);
}
if (e.NewElement != null)
{
try
{
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
}
if (status == PermissionStatus.Granted)
{
cameraPreview.OpenCamera(e.NewElement.Camera);
SetNativeControl(cameraPreview);
}
else if (status != PermissionStatus.Unknown)
{
//await DisplayAlert("Camera Denied", "Can not continue, try again.", "OK");
}
}
catch (Exception ex)
{
}
}
}
}
}
Android - NativeCameraView:
public sealed class NativeCameraView : FrameLayout, TextureView.ISurfaceTextureListener
{
private static readonly SparseIntArray Orientations = new SparseIntArray();
public event EventHandler<ImageSource> Photo;
public bool OpeningCamera { private get; set; }
public CameraDevice CameraDevice;
private readonly CameraStateListener _mStateListener;
private CaptureRequest.Builder _previewBuilder;
private CameraCaptureSession _previewSession;
private SurfaceTexture _viewSurface;
private readonly TextureView _cameraTexture;
private Size _previewSize;
private readonly Context _context;
private CameraManager _manager;
public NativeCameraView(Context context) : base(context)
{
_context = context;
var inflater = LayoutInflater.FromContext(context);
if (inflater == null) return;
var view = inflater.Inflate(Resource.Layout.CameraLayout, this);
_cameraTexture = view.FindViewById<TextureView>(Resource.Id.cameraTexture);
_cameraTexture.Click += (sender, args) => { TakePhoto(); };
_cameraTexture.SurfaceTextureListener = this;
_mStateListener = new CameraStateListener { Camera = this };
Orientations.Append((int)SurfaceOrientation.Rotation0, 0);
Orientations.Append((int)SurfaceOrientation.Rotation90, 90);
Orientations.Append((int)SurfaceOrientation.Rotation180, 180);
Orientations.Append((int)SurfaceOrientation.Rotation270, 270);
}
public void OnSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
_viewSurface = surface;
ConfigureTransform(width, height);
StartPreview();
}
public bool OnSurfaceTextureDestroyed(SurfaceTexture surface)
{
return true;
}
public void OnSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
{
}
public void OnSurfaceTextureUpdated(SurfaceTexture surface)
{
}
public void OpenCamera(CameraOptions options)
{
if (_context == null || OpeningCamera)
{
return;
}
OpeningCamera = true;
_manager = (CameraManager)_context.GetSystemService(Context.CameraService);
var cameraId = _manager.GetCameraIdList()[(int)options];
var characteristics = _manager.GetCameraCharacteristics(cameraId);
var map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap);
_previewSize = map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture)))[0];
_manager.OpenCamera(cameraId, _mStateListener, null);
}
private void TakePhoto()
{
if (_context == null || CameraDevice == null) return;
var characteristics = _manager.GetCameraCharacteristics(CameraDevice.Id);
Size[] jpegSizes = null;
if (characteristics != null)
{
jpegSizes = ((StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap)).GetOutputSizes((int)ImageFormatType.Jpeg);
}
var width = 480;
var height = 640;
if (jpegSizes != null && jpegSizes.Length > 0)
{
width = jpegSizes[0].Width;
height = jpegSizes[0].Height;
}
var reader = ImageReader.NewInstance(width, height, ImageFormatType.Jpeg, 1);
var outputSurfaces = new List<Surface>(2) { reader.Surface, new Surface(_viewSurface) };
var captureBuilder = CameraDevice.CreateCaptureRequest(CameraTemplate.StillCapture);
captureBuilder.AddTarget(reader.Surface);
captureBuilder.Set(CaptureRequest.ControlMode, new Integer((int)ControlMode.Auto));
var windowManager = _context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var rotation = windowManager.DefaultDisplay.Rotation;
captureBuilder.Set(CaptureRequest.JpegOrientation, new Integer(Orientations.Get((int)rotation)));
var readerListener = new ImageAvailableListener();
readerListener.Photo += (sender, buffer) =>
{
Photo?.Invoke(this, ImageSource.FromStream(() => new MemoryStream(buffer)));
};
var thread = new HandlerThread("CameraPicture");
thread.Start();
var backgroundHandler = new Handler(thread.Looper);
reader.SetOnImageAvailableListener(readerListener, backgroundHandler);
var captureListener = new CameraCaptureListener();
captureListener.PhotoComplete += (sender, e) =>
{
StartPreview();
};
CameraDevice.CreateCaptureSession(outputSurfaces, new CameraCaptureStateListener
{
OnConfiguredAction = session =>
{
try
{
_previewSession = session;
session.Capture(captureBuilder.Build(), captureListener, backgroundHandler);
}
catch (CameraAccessException ex)
{
Log.WriteLine(LogPriority.Info, "Capture Session error: ", ex.ToString());
}
}
}, backgroundHandler);
}
public void StartPreview()
{
if (CameraDevice == null || !_cameraTexture.IsAvailable || _previewSize == null) return;
var texture = _cameraTexture.SurfaceTexture;
texture.SetDefaultBufferSize(_previewSize.Width, _previewSize.Height);
var surface = new Surface(texture);
_previewBuilder = CameraDevice.CreateCaptureRequest(CameraTemplate.Preview);
_previewBuilder.AddTarget(surface);
CameraDevice.CreateCaptureSession(new List<Surface> { surface },
new CameraCaptureStateListener
{
OnConfigureFailedAction = session =>
{
},
OnConfiguredAction = session =>
{
_previewSession = session;
UpdatePreview();
}
},
null);
}
private void ConfigureTransform(int viewWidth, int viewHeight)
{
if (_viewSurface == null || _previewSize == null || _context == null) return;
var windowManager = _context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var rotation = windowManager.DefaultDisplay.Rotation;
var matrix = new Matrix();
var viewRect = new RectF(0, 0, viewWidth, viewHeight);
var bufferRect = new RectF(0, 0, _previewSize.Width, _previewSize.Height);
var centerX = viewRect.CenterX();
var centerY = viewRect.CenterY();
if (rotation == SurfaceOrientation.Rotation90 || rotation == SurfaceOrientation.Rotation270)
{
bufferRect.Offset(centerX - bufferRect.CenterX(), centerY - bufferRect.CenterY());
matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Fill);
matrix.PostRotate(90 * ((int)rotation - 2), centerX, centerY);
}
_cameraTexture.SetTransform(matrix);
}
private void UpdatePreview()
{
if (CameraDevice == null || _previewSession == null) return;
_previewBuilder.Set(CaptureRequest.ControlMode, new Integer((int)ControlMode.Auto));
var thread = new HandlerThread("CameraPreview");
thread.Start();
var backgroundHandler = new Handler(thread.Looper);
_previewSession.SetRepeatingRequest(_previewBuilder.Build(), null, backgroundHandler);
}
}
iOS:
[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewXamarinForms.iOS.Renderers
{
public class CameraViewRenderer : ViewRenderer<CameraView, NativeCameraView>
{
NativeCameraView uiCameraPreview;
protected override void OnElementChanged(ElementChangedEventArgs<CameraView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
uiCameraPreview = new NativeCameraView(e.NewElement.Camera);
SetNativeControl(uiCameraPreview);
}
if (e.OldElement != null)
{
// Unsubscribe
uiCameraPreview.Tapped -= OnCameraPreviewTapped;
}
if (e.NewElement != null)
{
// Subscribe
uiCameraPreview.Tapped += OnCameraPreviewTapped;
}
}
void OnCameraPreviewTapped(object sender, EventArgs e)
{
if (uiCameraPreview.IsPreviewing)
{
uiCameraPreview.CaptureSession.StopRunning();
uiCameraPreview.IsPreviewing = false;
}
else
{
uiCameraPreview.CaptureSession.StartRunning();
uiCameraPreview.IsPreviewing = true;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.CaptureSession.Dispose();
Control.Dispose();
}
base.Dispose(disposing);
}
}
}
iOS - NativeCameraView:
public class NativeCameraView : UIView
{
AVCaptureVideoPreviewLayer previewLayer;
CameraOptions cameraOptions;
public event EventHandler<EventArgs> Tapped;
public AVCaptureSession CaptureSession { get; private set; }
public bool IsPreviewing { get; set; }
public NativeCameraView(CameraOptions options)
{
cameraOptions = options;
IsPreviewing = false;
Initialize();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
previewLayer.Frame = rect;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
OnTapped();
}
protected virtual void OnTapped()
{
var eventHandler = Tapped;
if (eventHandler != null)
{
eventHandler(this, new EventArgs());
}
}
void Initialize()
{
CaptureSession = new AVCaptureSession();
previewLayer = new AVCaptureVideoPreviewLayer(CaptureSession)
{
Frame = Bounds,
VideoGravity = AVLayerVideoGravity.ResizeAspectFill
};
var videoDevices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
var cameraPosition = (cameraOptions == CameraOptions.Front) ? AVCaptureDevicePosition.Front : AVCaptureDevicePosition.Back;
var device = videoDevices.FirstOrDefault(d => d.Position == cameraPosition);
if (device == null)
{
return;
}
NSError error;
var input = new AVCaptureDeviceInput(device, out error);
CaptureSession.AddInput(input);
Layer.AddSublayer(previewLayer);
CaptureSession.StartRunning();
IsPreviewing = true;
}
}
这是它在 Android 模拟器上的样子
If you have any question I've created a repository on Github for this question, pls check it here
是否可以使用 xamarin.form 创建一个界面来捕捉或选择图像,就像在 Instagram 上一样。
这将是一张像 Instagram 上那样的方形图片。我测试了 MediaPlugin,但它只是打开相机,我无法将它插入到视图中。
您应该创建一个自定义渲染器。
首先在您的共享项目中创建一个视图,如下所示:
public enum CameraOptions
{
Rear,
Front
}
public class CameraView : View
{
public static readonly BindableProperty CameraProperty = BindableProperty.Create(nameof(Camera), typeof(CameraOptions), typeof(CameraView), CameraOptions.Rear);
public CameraOptions Camera
{
get { return (CameraOptions)GetValue(CameraProperty); }
set { SetValue(CameraProperty, value); }
}
}
然后为每个平台创建一个渲染器。
Android:
[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewXamarinForms.Droid.Renderers
{
public class CameraViewRenderer : ViewRenderer<CameraView, NativeCameraView>
{
NativeCameraView cameraPreview;
public CameraViewRenderer(Context context) : base(context)
{
}
protected override async void OnElementChanged(ElementChangedEventArgs<CameraView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
cameraPreview = new NativeCameraView(Context);
SetNativeControl(cameraPreview);
}
if (e.NewElement != null)
{
try
{
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
}
if (status == PermissionStatus.Granted)
{
cameraPreview.OpenCamera(e.NewElement.Camera);
SetNativeControl(cameraPreview);
}
else if (status != PermissionStatus.Unknown)
{
//await DisplayAlert("Camera Denied", "Can not continue, try again.", "OK");
}
}
catch (Exception ex)
{
}
}
}
}
}
Android - NativeCameraView:
public sealed class NativeCameraView : FrameLayout, TextureView.ISurfaceTextureListener
{
private static readonly SparseIntArray Orientations = new SparseIntArray();
public event EventHandler<ImageSource> Photo;
public bool OpeningCamera { private get; set; }
public CameraDevice CameraDevice;
private readonly CameraStateListener _mStateListener;
private CaptureRequest.Builder _previewBuilder;
private CameraCaptureSession _previewSession;
private SurfaceTexture _viewSurface;
private readonly TextureView _cameraTexture;
private Size _previewSize;
private readonly Context _context;
private CameraManager _manager;
public NativeCameraView(Context context) : base(context)
{
_context = context;
var inflater = LayoutInflater.FromContext(context);
if (inflater == null) return;
var view = inflater.Inflate(Resource.Layout.CameraLayout, this);
_cameraTexture = view.FindViewById<TextureView>(Resource.Id.cameraTexture);
_cameraTexture.Click += (sender, args) => { TakePhoto(); };
_cameraTexture.SurfaceTextureListener = this;
_mStateListener = new CameraStateListener { Camera = this };
Orientations.Append((int)SurfaceOrientation.Rotation0, 0);
Orientations.Append((int)SurfaceOrientation.Rotation90, 90);
Orientations.Append((int)SurfaceOrientation.Rotation180, 180);
Orientations.Append((int)SurfaceOrientation.Rotation270, 270);
}
public void OnSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
_viewSurface = surface;
ConfigureTransform(width, height);
StartPreview();
}
public bool OnSurfaceTextureDestroyed(SurfaceTexture surface)
{
return true;
}
public void OnSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
{
}
public void OnSurfaceTextureUpdated(SurfaceTexture surface)
{
}
public void OpenCamera(CameraOptions options)
{
if (_context == null || OpeningCamera)
{
return;
}
OpeningCamera = true;
_manager = (CameraManager)_context.GetSystemService(Context.CameraService);
var cameraId = _manager.GetCameraIdList()[(int)options];
var characteristics = _manager.GetCameraCharacteristics(cameraId);
var map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap);
_previewSize = map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture)))[0];
_manager.OpenCamera(cameraId, _mStateListener, null);
}
private void TakePhoto()
{
if (_context == null || CameraDevice == null) return;
var characteristics = _manager.GetCameraCharacteristics(CameraDevice.Id);
Size[] jpegSizes = null;
if (characteristics != null)
{
jpegSizes = ((StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap)).GetOutputSizes((int)ImageFormatType.Jpeg);
}
var width = 480;
var height = 640;
if (jpegSizes != null && jpegSizes.Length > 0)
{
width = jpegSizes[0].Width;
height = jpegSizes[0].Height;
}
var reader = ImageReader.NewInstance(width, height, ImageFormatType.Jpeg, 1);
var outputSurfaces = new List<Surface>(2) { reader.Surface, new Surface(_viewSurface) };
var captureBuilder = CameraDevice.CreateCaptureRequest(CameraTemplate.StillCapture);
captureBuilder.AddTarget(reader.Surface);
captureBuilder.Set(CaptureRequest.ControlMode, new Integer((int)ControlMode.Auto));
var windowManager = _context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var rotation = windowManager.DefaultDisplay.Rotation;
captureBuilder.Set(CaptureRequest.JpegOrientation, new Integer(Orientations.Get((int)rotation)));
var readerListener = new ImageAvailableListener();
readerListener.Photo += (sender, buffer) =>
{
Photo?.Invoke(this, ImageSource.FromStream(() => new MemoryStream(buffer)));
};
var thread = new HandlerThread("CameraPicture");
thread.Start();
var backgroundHandler = new Handler(thread.Looper);
reader.SetOnImageAvailableListener(readerListener, backgroundHandler);
var captureListener = new CameraCaptureListener();
captureListener.PhotoComplete += (sender, e) =>
{
StartPreview();
};
CameraDevice.CreateCaptureSession(outputSurfaces, new CameraCaptureStateListener
{
OnConfiguredAction = session =>
{
try
{
_previewSession = session;
session.Capture(captureBuilder.Build(), captureListener, backgroundHandler);
}
catch (CameraAccessException ex)
{
Log.WriteLine(LogPriority.Info, "Capture Session error: ", ex.ToString());
}
}
}, backgroundHandler);
}
public void StartPreview()
{
if (CameraDevice == null || !_cameraTexture.IsAvailable || _previewSize == null) return;
var texture = _cameraTexture.SurfaceTexture;
texture.SetDefaultBufferSize(_previewSize.Width, _previewSize.Height);
var surface = new Surface(texture);
_previewBuilder = CameraDevice.CreateCaptureRequest(CameraTemplate.Preview);
_previewBuilder.AddTarget(surface);
CameraDevice.CreateCaptureSession(new List<Surface> { surface },
new CameraCaptureStateListener
{
OnConfigureFailedAction = session =>
{
},
OnConfiguredAction = session =>
{
_previewSession = session;
UpdatePreview();
}
},
null);
}
private void ConfigureTransform(int viewWidth, int viewHeight)
{
if (_viewSurface == null || _previewSize == null || _context == null) return;
var windowManager = _context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
var rotation = windowManager.DefaultDisplay.Rotation;
var matrix = new Matrix();
var viewRect = new RectF(0, 0, viewWidth, viewHeight);
var bufferRect = new RectF(0, 0, _previewSize.Width, _previewSize.Height);
var centerX = viewRect.CenterX();
var centerY = viewRect.CenterY();
if (rotation == SurfaceOrientation.Rotation90 || rotation == SurfaceOrientation.Rotation270)
{
bufferRect.Offset(centerX - bufferRect.CenterX(), centerY - bufferRect.CenterY());
matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Fill);
matrix.PostRotate(90 * ((int)rotation - 2), centerX, centerY);
}
_cameraTexture.SetTransform(matrix);
}
private void UpdatePreview()
{
if (CameraDevice == null || _previewSession == null) return;
_previewBuilder.Set(CaptureRequest.ControlMode, new Integer((int)ControlMode.Auto));
var thread = new HandlerThread("CameraPreview");
thread.Start();
var backgroundHandler = new Handler(thread.Looper);
_previewSession.SetRepeatingRequest(_previewBuilder.Build(), null, backgroundHandler);
}
}
iOS:
[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewXamarinForms.iOS.Renderers
{
public class CameraViewRenderer : ViewRenderer<CameraView, NativeCameraView>
{
NativeCameraView uiCameraPreview;
protected override void OnElementChanged(ElementChangedEventArgs<CameraView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
uiCameraPreview = new NativeCameraView(e.NewElement.Camera);
SetNativeControl(uiCameraPreview);
}
if (e.OldElement != null)
{
// Unsubscribe
uiCameraPreview.Tapped -= OnCameraPreviewTapped;
}
if (e.NewElement != null)
{
// Subscribe
uiCameraPreview.Tapped += OnCameraPreviewTapped;
}
}
void OnCameraPreviewTapped(object sender, EventArgs e)
{
if (uiCameraPreview.IsPreviewing)
{
uiCameraPreview.CaptureSession.StopRunning();
uiCameraPreview.IsPreviewing = false;
}
else
{
uiCameraPreview.CaptureSession.StartRunning();
uiCameraPreview.IsPreviewing = true;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.CaptureSession.Dispose();
Control.Dispose();
}
base.Dispose(disposing);
}
}
}
iOS - NativeCameraView:
public class NativeCameraView : UIView
{
AVCaptureVideoPreviewLayer previewLayer;
CameraOptions cameraOptions;
public event EventHandler<EventArgs> Tapped;
public AVCaptureSession CaptureSession { get; private set; }
public bool IsPreviewing { get; set; }
public NativeCameraView(CameraOptions options)
{
cameraOptions = options;
IsPreviewing = false;
Initialize();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
previewLayer.Frame = rect;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
OnTapped();
}
protected virtual void OnTapped()
{
var eventHandler = Tapped;
if (eventHandler != null)
{
eventHandler(this, new EventArgs());
}
}
void Initialize()
{
CaptureSession = new AVCaptureSession();
previewLayer = new AVCaptureVideoPreviewLayer(CaptureSession)
{
Frame = Bounds,
VideoGravity = AVLayerVideoGravity.ResizeAspectFill
};
var videoDevices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
var cameraPosition = (cameraOptions == CameraOptions.Front) ? AVCaptureDevicePosition.Front : AVCaptureDevicePosition.Back;
var device = videoDevices.FirstOrDefault(d => d.Position == cameraPosition);
if (device == null)
{
return;
}
NSError error;
var input = new AVCaptureDeviceInput(device, out error);
CaptureSession.AddInput(input);
Layer.AddSublayer(previewLayer);
CaptureSession.StartRunning();
IsPreviewing = true;
}
}
这是它在 Android 模拟器上的样子
If you have any question I've created a repository on Github for this question, pls check it here