【问题标题】:How to create and implement a Android CustomOverlay for ZXing Scanner?如何为 ZXing Scanner 创建和实现 Android CustomOverlay?
【发布时间】:2019-08-01 06:57:49
【问题描述】:

我正在为我的 Xamarin.Forms 项目使用 ZXing 条码扫描器插件。根据一些帖子,我需要为 ZXing 扫描仪使用自定义覆盖才能获得取消按钮。但我不熟悉 Xamarin.Forms 项目中的 Android 开发。

到目前为止,我知道一个扫描仪(ZXing 插件的 MobileBarcodeScanner)接受一个 Android.Views.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;
}

但我不知道如何创建视图页面(在 XML 设计中或以编程方式)并将其设置为scanner.CustomOverlay。

我不确定是否可以将通用的 Android 视图用于扫描仪,任何详细的参考资料都会非常感激。

谢谢。

【问题讨论】:

    标签: android xamarin.forms xamarin.android zxing


    【解决方案1】:

    如果要自定义叠加层,则必须为每个平台创建自己的视图。您可以像这样自定义叠加层(创建一个名为 ZxingOverlayView 的自定义叠加层,将其设置为 scanner.CustomOverlay):

    var scanner = new ZXing.MobileMobileBarcodeScanner();
    scanner.UseCustomOverlay = true;
    myCustomOverlayInstance = new ZxingOverlayView(this, scanner);
    scanner.CustomOverlay = myCustomOverlayInstance;
    

    ZxingOverlayView 应该继承 view 然后将您的控件添加到自定义视图。 您可以查看有关此链接的详细信息。

    http://slackshotindustries.blogspot.com/2013/04/creating-custom-overlays-in-xzing.html

    编辑

    有完整的步骤。

    首先,你可以为依赖服务创建一个接口。

     public interface IDeviceService
      {
        Task<string> ScanAsync();
      }
    

    您可以在 PCL 代码中使用此接口。

      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");
            }
        }
    

    在安卓平台。你可以设计你的布局。首先,创建一个布局

    <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>
    

    然后实现依赖服务接口,使用你的新布局。

     [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 代码。

    [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;
        }
    }
    

    这里正在运行 GIF。

    这是一个很好的简单代码。你可以参考一下。 https://github.com/Wenfengcheng/ZXing.Sample_Xamarin

    【讨论】:

    • 我在创建这个帖子之前已经阅读了那篇文章,但我不明白那篇文章中写了什么。现在,我已经切换到 ZXing.Form 并导航到扫描页面,同时启用具有后退按钮的导航栏。我不确定这是否适用于 iOS。我想我稍后会深入研究这篇文章。感谢分享。
    • @solidcomer 我编辑了我的答案,我添加了实现布局的完整步骤,你可以参考一下。最后,有一个简单的实现方式
    • @solidcomer 演示代码中有IOS成就中的代码
    • 非常感谢!我上周标记了它,但忘记跟进评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多