I'm using ZXing barcode scanner plugin for my Xamarin.Forms project. According to some posts, I need to use custom overlay for ZXing scanner in order to have cancel button. But I'm not familiar with Android development in a Xamarin.Forms project.
So far, I know a scanner (MobileBarcodeScanner of ZXing plugin) accept an Android.Views.View as an overlay to replace the default scanning page view.
public async Task<string> ScanAsync()
{
MobileBarcodeScanner scanner = new MobileBarcodeScanner();
scanner.UseCustomOverlay = true;
scanner.CustomOverlay = ???????;
var scanResult = await scanner.Scan();
if (scanResult != null)
{ return scanResult.Text; }
return null;
}
But I don't know how to create a view page (either in XML design or programmatically) and set it to scanner.CustomOverlay.
I'm not sure if a common Android view can be used for scanner, any detailed reference would be really appreicated.
Thanks.
If you want to customize the overlay, you must create your own View for each platform. You can customize your overlay like this( create a custom overlay called ZxingOverlayView
set it to the scanner.CustomOverlay
):
var scanner = new ZXing.MobileMobileBarcodeScanner();
scanner.UseCustomOverlay = true;
myCustomOverlayInstance = new ZxingOverlayView(this, scanner);
scanner.CustomOverlay = myCustomOverlayInstance;
ZxingOverlayView
should inherit the view
then add your control to the custom view.
You can see details about this link.
http://slackshotindustries.blogspot.com/2013/04/creating-custom-overlays-in-xzing.html
Edit
There are completely steps.
First of all, you could create a interface for dependence service.
public interface IDeviceService
{
Task<string> ScanAsync();
}
You can use this interface in the PCL code.
private async void DependencyButton_Clicked(object sender, EventArgs e)
{
var result = await DependencyService.Get<IDeviceService>().ScanAsync();
if (!string.IsNullOrEmpty(result))
{
await DisplayAlert(result, null, "OK");
}
}
In the android platform. You can design your layout. First of all, create a layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imgClose"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/close"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"/>
<RelativeLayout
android:id="@+id/llScan"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_centerInParent="true"
android:background="@drawable/scan">
<ImageView
android:id="@+id/imgLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/scan_line"
android:layout_centerHorizontal="true" />
</RelativeLayout>
<View
android:id="@+id/viewTop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/llScan"
android:background="@color/title_black"/>
<View
android:id="@+id/viewBottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/llScan"
android:background="@color/title_black"
android:layout_alignParentBottom="true"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/viewTop"
android:layout_toLeftOf="@id/llScan"
android:layout_above="@id/viewBottom"
android:background="@color/title_black"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/viewTop"
android:layout_above="@id/viewBottom"
android:layout_toRightOf="@id/llScan"
android:background="@color/title_black"/>
</RelativeLayout>
Then achieve the dependence service interface and use your new layout.
[assembly: Xamarin.Forms.Dependency(typeof(DeviceService))]
namespace Sample.Droid
{
public class DeviceService : IDeviceService
{
public async Task<string> ScanAsync()
{
var scanner = new ZXing.Mobile.MobileBarcodeScanner
{
UseCustomOverlay = true
};
//scanner.CustomOverlay = new CustomScanView(Application.Context);
var options = new ZXing.Mobile.MobileBarcodeScanningOptions()
{
TryHarder = true,
AutoRotate = false,
UseFrontCameraIfAvailable = false,
CameraResolutionSelector = new CameraResolutionSelectorDelegate(SelectLowestResolutionMatchingDisplayAspectRatio),
PossibleFormats = new List<ZXing.BarcodeFormat>()
{
ZXing.BarcodeFormat.QR_CODE
}
};
View scanView = LayoutInflater.From(Application.Context).Inflate(Resource.Layout.ScanView, null);
ImageView imgLine = scanView.FindViewById<ImageView>(Resource.Id.imgLine);
ImageView imgClose = scanView.FindViewById<ImageView>(Resource.Id.imgClose);
imgClose.Click += delegate
{
scanner.Cancel();
};
scanner.CustomOverlay = scanView;
ObjectAnimator objectAnimator = ObjectAnimator.OfFloat(imgLine, "Y", 0, DpToPixels(240));
objectAnimator.SetDuration(2500);
objectAnimator.RepeatCount = -1;
objectAnimator.SetInterpolator(new LinearInterpolator());
objectAnimator.RepeatMode = ValueAnimatorRepeatMode.Restart;
objectAnimator.Start();
ZXing.Result scanResults = await scanner.Scan(CrossCurrentActivity.Current.Activity, options);
if (scanResults != null)
{
return scanResults.Text;
}
return string.Empty;
}
private int DpToPixels(double dp)
{
return (int)(dp * Application.Context.Resources.DisplayMetrics.Density);
}
private CameraResolution SelectLowestResolutionMatchingDisplayAspectRatio(List<CameraResolution> availableResolutions)
{
CameraResolution result = null;
//a tolerance of 0.1 should not be visible to the user
double aspectTolerance = 0.1;
var displayOrientationHeight = DeviceDisplay.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? DeviceDisplay.MainDisplayInfo.Height : DeviceDisplay.MainDisplayInfo.Width;
var displayOrientationWidth = DeviceDisplay.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? DeviceDisplay.MainDisplayInfo.Width : DeviceDisplay.MainDisplayInfo.Height;
//calculatiing our targetRatio
var targetRatio = displayOrientationHeight / displayOrientationWidth;
var targetHeight = displayOrientationHeight;
var minDiff = double.MaxValue;
//camera API lists all available resolutions from highest to lowest, perfect for us
//making use of this sorting, following code runs some comparisons to select the lowest resolution that matches the screen aspect ratio and lies within tolerance
//selecting the lowest makes Qr detection actual faster most of the time
foreach (var r in availableResolutions.Where(r => Math.Abs(((double)r.Width / r.Height) - targetRatio) < aspectTolerance))
{
//slowly going down the list to the lowest matching solution with the correct aspect ratio
if (Math.Abs(r.Height - targetHeight) < minDiff)
minDiff = Math.Abs(r.Height - targetHeight);
result = r;
}
return result;
}
}
}
IOS code.
[assembly: Xamarin.Forms.Dependency(typeof(DeviceService))]
namespace Sample.iOS
{
public class DeviceService : IDeviceService
{
public async Task<string> ScanAsync()
{
var scanner = new ZXing.Mobile.MobileBarcodeScanner()
{
UseCustomOverlay = true
};
var options = new ZXing.Mobile.MobileBarcodeScanningOptions()
{
TryHarder = true,
AutoRotate = false,
UseFrontCameraIfAvailable = false,
PossibleFormats = new List<ZXing.BarcodeFormat>()
{
ZXing.BarcodeFormat.QR_CODE
}
};
ScannerOverlayView customOverlay = new ScannerOverlayView();
customOverlay.OnCancel += () =>
{
scanner?.Cancel();
};
customOverlay.OnResume += () =>
{
scanner?.ResumeAnalysis();
};
customOverlay.OnPause += () =>
{
scanner?.PauseAnalysis();
};
scanner.CustomOverlay = customOverlay;
ZXing.Result scanResults = null;
scanResults = await scanner.Scan(options);
//customOverlay.Dispose();
if (scanResults != null)
{
return scanResults.Text;
}
return string.Empty;
}
}
Here is running GIF.
Here is a good simple code. you could refer to it. https://github.com/Wenfengcheng/ZXing.Sample_Xamarin