这是代码(运行此代码以查看问题所在) => github/x0axz/CustomRenderer
在我的Xamarin应用程序中,有一个用于Android的自定义相机渲染器,它是通过 MessagingCenter从 ViewModel 调用的./code>.
In my Xamarin App, there is a Custom Camera Renderer for Android, which is being called from ViewModel through MessagingCenter.Send<object>(this, "A");.
一切正常.唯一的问题是,它在第一页上显示图片,但是当我导航到另一页时,还有另一个命令 MessagingCenter.Send< object>(this,"A"); ,以拍摄照片,但这一次它返回错误在调用Camera.release()之后正在使用相机.
It's working fine. The only problem is that, on first page it takes Picture, but when I navigate to another page, where there is an other command, MessagingCenter.Send<object>(this, "A");, to take a Picture, but this time it returns an error Camera is being used after Camera.release() was called.
下面是ViewModel和Camera Renderer的代码.
Below is the code for ViewModel and Camera Renderer.
FirstCameraViewModel.cs
private void FirstCamera(object sender, EventArgs e) { try { MessagingCenter.Send<object>(this, "A"); } catch { Console.WriteLine(error.InnerException.StackTrace); Console.WriteLine(error.InnerException.Message); } finally { timer_countdown = new Timer(); timer_countdown.Interval = 1000; timer_countdown.Elapsed += OnTimedEvent; timer_countdown.Enabled = true; timer_countdown.AutoReset = true; timer_countdown.Start(); } } private void OnTimedEvent(object source, ElapsedEventArgs e) { Seconds++; if (Seconds == 5) { MainThread.BeginInvokeOnMainThread(async () => { await NavigationService.NavigateToAsync<SecondCameraViewModel>(); }); } }SecondCameraViewModel.cs,在此页面中返回错误
private void SecondCamera(object sender, EventArgs e) { try { MessagingCenter.Send<object>(this, "A"); } catch { Console.WriteLine(error.InnerException.StackTrace); Console.WriteLine(error.InnerException.Message); } finally { timer_countdown = new Timer(); timer_countdown.Interval = 1000; timer_countdown.Elapsed += OnTimedEvent; timer_countdown.Enabled = true; timer_countdown.AutoReset = true; timer_countdown.Start(); } } private void OnTimedEvent(object source, ElapsedEventArgs e) { Seconds++; if (Seconds == 5) { MainThread.BeginInvokeOnMainThread(async () => { await NavigationService.NavigateToAsync<IndexViewModel>(); }); } }更新
CameraPreview.cs
public sealed class CameraPreview : ViewGroup, ISurfaceHolderCallback, Camera.IPictureCallback { SurfaceView surfaceView; ISurfaceHolder holder; Camera.Size previewSize; IList<Camera.Size> supportedPreviewSizes; Camera camera; IWindowManager windowManager; public bool IsPreviewing { get; set; } public Camera Preview { get { return camera; } set { camera = value; if (camera != null) { supportedPreviewSizes = Preview.GetParameters().SupportedPreviewSizes; RequestLayout(); } } } public CameraPreview(Context context) : base(context) { surfaceView = new SurfaceView(context); AddView(surfaceView); windowManager = Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>(); IsPreviewing = false; holder = surfaceView.Holder; holder.AddCallback(this); MessagingCenter.Subscribe<object>(this, "A", (e) => { camera.TakePicture(null, null, this); }); } protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = ResolveSize(SuggestedMinimumWidth, widthMeasureSpec); int height = ResolveSize(SuggestedMinimumHeight, heightMeasureSpec); SetMeasuredDimension(width, height); if (supportedPreviewSizes != null) { previewSize = GetOptimalPreviewSize(supportedPreviewSizes, width, height); } } protected override void OnLayout(bool changed, int l, int t, int r, int b) { var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly); var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly); surfaceView.Measure(msw, msh); surfaceView.Layout(0, 0, r - l, b - t); } public void SurfaceCreated(ISurfaceHolder holder) { try { if (Preview != null) { Preview.SetPreviewDisplay(holder); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(@" ERROR: ", ex.Message); } } public void SurfaceDestroyed(ISurfaceHolder holder) { if (Preview != null) { Preview.StopPreview(); } } public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height) { var parameters = Preview.GetParameters(); parameters.SetPreviewSize(previewSize.Width, previewSize.Height); RequestLayout(); switch (windowManager.DefaultDisplay.Rotation) { case SurfaceOrientation.Rotation0: camera.SetDisplayOrientation(90); break; case SurfaceOrientation.Rotation90: camera.SetDisplayOrientation(0); break; case SurfaceOrientation.Rotation270: camera.SetDisplayOrientation(180); break; } Preview.SetParameters(parameters); Preview.StartPreview(); IsPreviewing = true; } Camera.Size GetOptimalPreviewSize(IList<Camera.Size> sizes, int w, int h) { const double AspectTolerance = 0.1; double targetRatio = (double)w / h; if (sizes == null) { return null; } Camera.Size optimalSize = null; double minDiff = double.MaxValue; int targetHeight = h; foreach (Camera.Size size in sizes) { double ratio = (double)size.Width / size.Height; if (Math.Abs(ratio - targetRatio) > AspectTolerance) continue; if (Math.Abs(size.Height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.Abs(size.Height - targetHeight); } } if (optimalSize == null) { minDiff = double.MaxValue; foreach (Camera.Size size in sizes) { if (Math.Abs(size.Height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.Abs(size.Height - targetHeight); } } } return optimalSize; } public void OnPictureTaken(byte[] data, Camera camera) { camera.StopPreview(); FileOutputStream outStream = null; File dataDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim); if (data != null) { try { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); var s = ts.TotalMilliseconds; outStream = new FileOutputStream(dataDir + "/" + s + ".jpg"); outStream.Write(data); outStream.Close(); } catch (FileNotFoundException e) { System.Console.Out.WriteLine(e.Message); } catch (IOException ie) { System.Console.Out.WriteLine(ie.Message); } } camera.StartPreview(); } }CameraPreviewRenderer.cs
public class CameraPreviewRenderer : ViewRenderer<Mobile.App.CameraPreview, CameraPreview> { CameraPreview cameraPreview; public CameraPreviewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Mobile.App.CameraPreview> e) { base.OnElementChanged(e); if (e.OldElement != null) { // Unsubscribe cameraPreview.Click -= OnCameraPreviewClicked; } if (e.NewElement != null) { if (Control == null) { cameraPreview = new CameraPreview(Context); SetNativeControl(cameraPreview); } Control.Preview = Camera.Open((int)e.NewElement.Camera); // Subscribe cameraPreview.Click += OnCameraPreviewClicked; } } void OnCameraPreviewClicked(object sender, EventArgs e) { if (cameraPreview.IsPreviewing) { cameraPreview.Preview.StopPreview(); cameraPreview.IsPreviewing = false; } else { cameraPreview.Preview.StartPreview(); cameraPreview.IsPreviewing = true; } } protected override void Dispose(bool disposing) { if (disposing) { Control.Preview.Release(); } base.Dispose(disposing); } } 推荐答案您将必须取消订阅MessagingCenter.
You will have to Unsubscribe the MessagingCenter.
共享项目中的CameraPreview.cs
CameraPreview.cs in shared project
public event EventHandler Subscribe; public event EventHandler Unsubscribe; public void OnSubscribe() { if (Subscribe != null) { Subscribe(this, EventArgs.Empty); } } public void OnUnsubscribe() { if (Unsubscribe != null) { Unsubscribe(this, EventArgs.Empty); } }MainPage.xaml
MainPage.xaml
<local:CameraPreview Camera="Rear" x:Name="CameraPreview" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />MainPage.xaml.cs
MainPage.xaml.cs
protected override void OnAppearing() { base.OnAppearing(); CameraPreview.OnSubscribe(); } protected override void OnDisappearing() { base.OnDisappearing(); CameraPreview.OnUnsubscribe(); }FirstCameraPage.xaml
FirstCameraPage.xaml
<local:CameraPreview x:Name="CameraPreview" Camera="Rear" HorizontalOptions="CenterAndExpand" VerticalOptions="FillAndExpand" WidthRequest="250"/>FirstCameraPage.xaml.cs
FirstCameraPage.xaml.cs
protected override void OnAppearing() { base.OnAppearing(); CameraPreview.OnSubscribe(); } protected override void OnDisappearing() { base.OnDisappearing(); CameraPreview.OnUnsubscribe(); }android项目中的CameraViewRenderer.cs
CameraViewRenderer.cs in android project
protected override void OnElementChanged(ElementChangedEventArgs<CustomRenderer.CameraPreview> e) { base.OnElementChanged(e); if (e.OldElement != null) { // Unsubscribe cameraPreview.Click -= OnCameraPreviewClicked; } if (e.NewElement != null) { if (Control == null) { cameraPreview = new CameraPreview(Context); SetNativeControl(cameraPreview); } Control.Preview = Camera.Open(); e.NewElement.Subscribe += (sender, e) => { Control.Subscribe(); }; e.NewElement.Unsubscribe += (sender, e) => { Control.Unsubscribe(); }; // Subscribe cameraPreview.Click += OnCameraPreviewClicked; } }Android项目中的CameraPreview.cs,添加以下方法.
CameraPreview.cs in android project, Add below methods.
internal void Subscribe() { MessagingCenter.Subscribe<object>(this, "A", (e) => { this.Preview.TakePicture(null, null, this); }); } internal void Unsubscribe() { MessagingCenter.Unsubscribe<object>(this, "A"); }并从构造函数中删除MessagingCenter订阅
and remove MessagingCenter subscription from constructor
public CameraPreview (Context context) : base (context) { surfaceView = new SurfaceView (context); AddView (surfaceView); windowManager = Context.GetSystemService (Context.WindowService).JavaCast<IWindowManager> (); IsPreviewing = false; holder = surfaceView.Holder; holder.AddCallback (this); //MessagingCenter.Subscribe<object>(this, "A", (e) => //{ // this.Preview.TakePicture(null, null, this); //}); }更多推荐
Xamarin中的调用目标已引发异常
发布评论