Xamarin.FormsでZXing.Net.Mobileを使ってみた

#はじめに
バーコードの読み取りをしたく、ZXing.Net.MobileをXamarin.Formsで使ってみました。PCLにそのままパッケージを追加することができないので、DependencyServiceを使って実装します。

#Xamarin.Forms
* 共有コードにインターフェースを作ります。スキャンだけするのでScanAsync()を書いておきます。

```csharp:IScanner.cs
using System;
using System.Threading.Tasks;

namespace ZXingNetMobileSample
{
    public interface IScanner
    {
        Task<string> ScanAsync();
    }
}
```

# Android
* パッケージにZXing.Net.Mobileを追加します。
* Required Permissionの中のCameraにチェックを入れます。
* IScannerインターフェースを継承したScanner_Androidクラスを実装します。

```csharp:Scanner_Android.cs
using System;
using System.Threading.Tasks;
using ZXing.Mobile;

[assembly: Xamarin.Forms.Dependency (typeof (ZXingNetMobileSample.Android.Scanner_Android))]

namespace ZXingNetMobileSample.Android
{
    public class Scanner_Android : IScanner
    {
        public async Task<string> ScanAsync()
        {
            var scanner = new MobileBarcodeScanner(Xamarin.Forms.Forms.Context);
            var result = await scanner.Scan();

            if (result != null)
            {
                return result.Text;
            }
            return string.Empty;
        }
    }
}
```
MobileBarcodeScannerに渡すContextはglobal::Android.App.Application.Contextではなく、Xamarin.Forms.Froms.Contextを使わないと、

> [MonoDroid] android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

というエラーを出してしまいます。

#iOS
iOSでもAndroidとほぼ同じく、
* パッケージにZXing.Net.Mobileを追加します。
* IScannerインターフェースを継承したScanner_Androidクラスを実装します。

```csharp:Scanner_iOS.cs
using System;
using System.Threading.Tasks;
using ZXing.Mobile;

[assembly: Xamarin.Forms.Dependency (typeof (ZXingNetMobileSample.iOS.Scanner_iOS))]

namespace ZXingNetMobileSample.iOS
{
    public class Scanner_iOS : IScanner
    {
        public async Task<string> ScanAsync()
        {
            var scanner = new MobileBarcodeScanner();
            var result = await scanner.Scan();

            if (result != null)
            {
                return result.Text;
            }
            return string.Empty;
        }
    }
}
```
Context渡すとかしなくていいみたいですが、実機で動かす環境が無いため、実際にバーコードが読み取れるかはやっていません。

#使い方
* DependencyService.Get&lt;IScanner&gt;().ScanAsync()を呼ぶと、カメラ画面が表示され、スキャンすることができます。

```csharp:App.cs
using System;
using Xamarin.Forms;

namespace ZXingNetMobileSample
{
    public class App
    {
        public static Page GetMainPage()
        {	
            var entry = new Entry();
            var button = new Button();
            button.Text = "Scan";
            button.Clicked += async (s, e) =>
            {
                entry.Text = await DependencyService.Get<IScanner>().ScanAsync();
            };

            return new ContentPage
            { 
                Content = new StackLayout
                {
                    Children =
                    {
                        entry,
                        button,
                    },
                    Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0),
                },
            };
        }
    }
}
```

#おわりに
簡単にできてしまったので、DependencyServiceのサンプルとなってしまいました。これで書籍裏表紙のバーコードからISBNを読み取ろうとすると、下のバーコードを読み取ってしまうので何とかしたいところです。

* [サンプル（ZXingNetMobileSample）](https://github.com/umicho/ZXingNetMobileSample)
* [ZXing.Net.Mobile / Components / Xamarin](https://components.xamarin.com/view/zxing.net.mobile)
* [Accessing Native Features via the DependencyService | Xamarin](http://developer.xamarin.com/guides/cross-platform/xamarin-forms/dependency-service/)