Custom Camera View in Xamarin Forms


Isn't it fun,

to instantly listen to a blog on the go? PLAY !

 
 

Custom_Camera_View_Xamarin_Forms

In Xamarin forms, android and iOS both can provide prebuilt camera and media control. Using a custom camera, we can change the camera setting as our need. For creating a custom camera, we need to create a renderer for specific platforms.

In this blog, we will see how to create a custom camera view in Xamarin forms.

First, open a visual studio and start your application. After starting your application create a class file for custom camera view and add the below code.

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

namespace CameraViewExample
{
    public class CameraView : View
    {
        public static readonly BindableProperty CamProp = BindableProperty.Create
            (propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraView), 
            defaultValue: CameraOptions.Rear);

        public CameraOptions Camera
        {
            get { return (CameraOptions)GetValue(CamProp); }
            set { SetValue(CamProp, value); }
        }
    }
}

This custom control is created in the .NET standard library project. If you don’t specify the camera property while creation of control that it will default specify to Rear camera.

After that, we need to create one more class file and make it into an enum file like below.

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

namespace CameraViewExample
{
    public class CameraView : View
    {
        public static readonly BindableProperty CamProp = BindableProperty.Create
            (propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraView), 
            defaultValue: CameraOptions.Rear);

        public CameraOptions Camera
        {
            get { return (CameraOptions)GetValue(CamProp); }
            set { SetValue(CamProp, value); }
        }
    }
}

Here, we can create an enum file for the camera option so that user can select the rear camera or front camera.



Now, we can create a design for a custom camera view. We can use the custom camera view by adding the reference in the XAML file. Add the namespace for the location of the custom camera view in XAML.




    
        
            
            
        
        
            
        
        
        

        
            
    

We need to implement a renderer in specific platforms. For creating renderer file in android right-click on android platform -> Add -> Class.

CameraViewRenderer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms.Platform.Android;

namespace CameraViewExample.Droid
{
    public class CameraViewRenderer : ViewRenderer
    {
        CameraView cameraPreview;

        public CameraViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                cameraPreview.Click -= OnCameraPreviewClicked;
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    cameraPreview = new CameraView(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);
        }
    }
}
CameraView.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace CameraViewExample.Droid
{
    public class CameraView : ViewGroup, ISurfaceHolderCallback
	{
		SurfaceView Sview;
		Camera.Size Sizeofpreview;
		Camera camera;
		IList SupoortedSize;
		IWindowManager windowManager;
		ISurfaceHolder Surfacehold;		

		public bool IsPreviewSet { get; set; }

		public Camera Preview
		{
			get { return camera; }
			set
			{
				camera = value;
				if (camera != null)
				{
					SupoortedSize = Preview.GetParameters().SupportedPreviewSizes;
					RequestLayout();
				}
			}
		}

		public CameraView(Context context)
			: base(context)
		{
			Sview = new SurfaceView(context);
			AddView(Sview);

			windowManager = Context.GetSystemService(Context.WindowService).JavaCast();

			IsPreviewSet = false;
			Surfacehold = Sview.Holder;
			Surfacehold.AddCallback(this);
		}

		protected override void OnMeasure(int Measurewidth, int Measureheight)
		{
			int width = ResolveSize(SuggestedMinimumWidth, Measurewidth);
			int height = ResolveSize(SuggestedMinimumHeight, Measureheight);
			SetMeasuredDimension(width, height);

			if (SupoortedSize != null)
			{
			Sizeofpreview = GetOptimalPreviewSize(SupoortedSize, width, height);
			}
		}

		protected override void OnLayout(bool changed, int label, int text, int rgb, int bold)
		{
			var makemesure = MeasureSpec.MakeMeasureSpec(rgb - label, MeasureSpecMode.Exactly);
			var makemesures = MeasureSpec.MakeMeasureSpec(bold - text, MeasureSpecMode.Exactly);

			Sview.Measure(makemesure, makemesures);
			Sview.Layout(0, 0, rgb - label, bold - text);
		}

		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 Surfacehold, Format Androidformat, int Surfacewidth, int Surfaceheight)
		{
			var parameters = Preview.GetParameters();
			parameters.SetPreviewSize(Sizeofpreview.Width, Sizeofpreview.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();
			IsPreviewSet = true;
		}

		Camera.Size GetOptimalPreviewSize(IList 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;
		}
	}
}

Planning to Hire Xamarin App Development Company ?

Your Search ends here.


Creating the Custom renderer for iOS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CameraViewExample;
using CameraViewExample.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewExample.iOS
{
    public class CameraViewRenderer : ViewRenderer
    {
        UICameraPreview uiCameraPreview;

        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                uiCameraPreview.Tapped -= OnCameraPreviewTapped;
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    uiCameraPreview = new UICameraPreview(e.NewElement.Camera);
                    SetNativeControl(uiCameraPreview);
                }
                // 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;
            }
        }
    }
}

Conclusion

In this blog, we have seen that Xamarin forms can provide the functionality to create an instance of native control for each platform using renderer. The ViewRenderer class on the android platform can instantiate a native View control. On the android platform, some controls are fast renderers that don’t consume the ViewRenderer class.

Custom Camera View in Xamarin Forms

Custom_Camera_View_Xamarin_Forms

In Xamarin forms, android and iOS both can provide prebuilt camera and media control. Using a custom camera, we can change the camera setting as our need. For creating a custom camera, we need to create a renderer for specific platforms.

In this blog, we will see how to create a custom camera view in Xamarin forms.

First, open a visual studio and start your application. After starting your application create a class file for custom camera view and add the below code.

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

namespace CameraViewExample
{
    public class CameraView : View
    {
        public static readonly BindableProperty CamProp = BindableProperty.Create
            (propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraView), 
            defaultValue: CameraOptions.Rear);

        public CameraOptions Camera
        {
            get { return (CameraOptions)GetValue(CamProp); }
            set { SetValue(CamProp, value); }
        }
    }
}

This custom control is created in the .NET standard library project. If you don’t specify the camera property while creation of control that it will default specify to Rear camera.

After that, we need to create one more class file and make it into an enum file like below.

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

namespace CameraViewExample
{
    public class CameraView : View
    {
        public static readonly BindableProperty CamProp = BindableProperty.Create
            (propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraView), 
            defaultValue: CameraOptions.Rear);

        public CameraOptions Camera
        {
            get { return (CameraOptions)GetValue(CamProp); }
            set { SetValue(CamProp, value); }
        }
    }
}

Here, we can create an enum file for the camera option so that user can select the rear camera or front camera.



Now, we can create a design for a custom camera view. We can use the custom camera view by adding the reference in the XAML file. Add the namespace for the location of the custom camera view in XAML.




    
        
            
            
        
        
            
        
        
        

        
            
    

We need to implement a renderer in specific platforms. For creating renderer file in android right-click on android platform -> Add -> Class.

CameraViewRenderer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms.Platform.Android;

namespace CameraViewExample.Droid
{
    public class CameraViewRenderer : ViewRenderer
    {
        CameraView cameraPreview;

        public CameraViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                cameraPreview.Click -= OnCameraPreviewClicked;
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    cameraPreview = new CameraView(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);
        }
    }
}
CameraView.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace CameraViewExample.Droid
{
    public class CameraView : ViewGroup, ISurfaceHolderCallback
	{
		SurfaceView Sview;
		Camera.Size Sizeofpreview;
		Camera camera;
		IList SupoortedSize;
		IWindowManager windowManager;
		ISurfaceHolder Surfacehold;		

		public bool IsPreviewSet { get; set; }

		public Camera Preview
		{
			get { return camera; }
			set
			{
				camera = value;
				if (camera != null)
				{
					SupoortedSize = Preview.GetParameters().SupportedPreviewSizes;
					RequestLayout();
				}
			}
		}

		public CameraView(Context context)
			: base(context)
		{
			Sview = new SurfaceView(context);
			AddView(Sview);

			windowManager = Context.GetSystemService(Context.WindowService).JavaCast();

			IsPreviewSet = false;
			Surfacehold = Sview.Holder;
			Surfacehold.AddCallback(this);
		}

		protected override void OnMeasure(int Measurewidth, int Measureheight)
		{
			int width = ResolveSize(SuggestedMinimumWidth, Measurewidth);
			int height = ResolveSize(SuggestedMinimumHeight, Measureheight);
			SetMeasuredDimension(width, height);

			if (SupoortedSize != null)
			{
			Sizeofpreview = GetOptimalPreviewSize(SupoortedSize, width, height);
			}
		}

		protected override void OnLayout(bool changed, int label, int text, int rgb, int bold)
		{
			var makemesure = MeasureSpec.MakeMeasureSpec(rgb - label, MeasureSpecMode.Exactly);
			var makemesures = MeasureSpec.MakeMeasureSpec(bold - text, MeasureSpecMode.Exactly);

			Sview.Measure(makemesure, makemesures);
			Sview.Layout(0, 0, rgb - label, bold - text);
		}

		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 Surfacehold, Format Androidformat, int Surfacewidth, int Surfaceheight)
		{
			var parameters = Preview.GetParameters();
			parameters.SetPreviewSize(Sizeofpreview.Width, Sizeofpreview.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();
			IsPreviewSet = true;
		}

		Camera.Size GetOptimalPreviewSize(IList 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;
		}
	}
}

Planning to Hire Xamarin App Development Company ?

Your Search ends here.


Creating the Custom renderer for iOS:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CameraViewExample;
using CameraViewExample.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CameraView), typeof(CameraViewRenderer))]
namespace CameraViewExample.iOS
{
    public class CameraViewRenderer : ViewRenderer
    {
        UICameraPreview uiCameraPreview;

        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                uiCameraPreview.Tapped -= OnCameraPreviewTapped;
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    uiCameraPreview = new UICameraPreview(e.NewElement.Camera);
                    SetNativeControl(uiCameraPreview);
                }
                // 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;
            }
        }
    }
}

Conclusion

In this blog, we have seen that Xamarin forms can provide the functionality to create an instance of native control for each platform using renderer. The ViewRenderer class on the android platform can instantiate a native View control. On the android platform, some controls are fast renderers that don’t consume the ViewRenderer class.