Xamarin.FormsにはQRコードやバーコードを読み取るためのZXing.Net.Mobileというライブラリがあります。
今回はこのライブラリを使用して、Xamarin.FormsでQRコードをスキャンして表示する簡単なアプリを作ってみました。
環境
Visual Studio Community 2017 for Mac
Xamarin.Forms (3.6.0.220655)
ZXing.Net.Mobile (2.4.1)
ZXing.Net.Mobile.Forms (2.4.1)
手順
準備
それぞれのプロジェクト(.Net Standard、iOS、Android)に以下のNuGet Packageをインストールします。
- ZXing.Net.Mobile
- ZXing.Net.Mobile.Forms
次にAndroidのMainActivity.csとiOSのAppDelegate.csにコードの追加を行います。
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { (省略) //追加 ZXing.Net.Mobile.Forms.Android.Platform.Init(); LoadApplication(new App()); } //追加 public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } }
AppDelegate.cs
[Register("AppDelegate")] public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); //追加 ZXing.Net.Mobile.Forms.iOS.Platform.Init(); LoadApplication(new App()); return base.FinishedLaunching(app, options); } }
次に端末でカメラを使用するための準備をします。
Android:AndroidManifest.xmlの「必要なアクセス許可」の「カメラ」にチェックを入れる
iOS:info.plistを開き、以下の項目を追加する
* プロパティ:プライバシー-カメラの利用状況の説明
* 値:"カメラを利用してスキャンします"
最後にナビゲーションページを使うためにApp.xaml.csに以下の変更を行います。
public App() { InitializeComponent(); //ここを変更 MainPage = new NavigationPage(new MainPage()); }
準備はこれでOKです。それでは画面を作っていきましょう。
画面作成と処理の追加
それではアプリを作っていきましょう。ページは次の2つだけです。
- MainPage:中央にボタンがあって、それを押すとスキャンするページ(QRScanPage)に移動
- QRScanPage:読み取りを行うと画面中央に読み取った値をアラートに表示する OKボタンを押すと読み取りを再開
最初にMainPageです。このページはボタンを押して次のページに行くだけです。
MainPage.xaml
<?xml version="1.0" encoding="utf-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:QRCodeStudy" x:Class="QRCodeStudy.MainPage"> <StackLayout> <Button Text="QRコード読み取り" Clicked="OnQR" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> </StackLayout> </ContentPage>
MainPage.xaml.cs
using System; using Xamarin.Forms; namespace QRCodeStudy { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } void OnQR(object sender, EventArgs e) { Navigation.PushAsync(new QRScanPage()); } } }
次に読み取りページを作っていきます。
QRScanPage.xaml
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="QRCodeStudy.QRScanPage" xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"> <ContentPage.Content> <Grid> <zxing:ZXingScannerView x:Name="zxing" OnScanResult="Handle_OnScanResult"/> <zxing:ZXingDefaultOverlay /> </Grid> </ContentPage.Content> </ContentPage>
ZXingScannerViewのOnScanResultイベントで読み取った値をアラートに表示するように設定します。
QRScanPage.xaml.cs
using Xamarin.Forms; namespace QRCodeStudy { public partial class QRScanPage : ContentPage { public QRScanPage() { InitializeComponent(); } void Handle_OnScanResult(ZXing.Result result) { Device.BeginInvokeOnMainThread(async () => { zxing.IsAnalyzing = false; //読み取り停止 await DisplayAlert("通知","次の値を読み取りました:" + result.Text,"OK"); zxing.IsAnalyzing = true; //読み取り再開 }); } protected override void OnAppearing() { base.OnAppearing(); zxing.IsScanning = true; } protected override void OnDisappearing() { zxing.IsScanning = false; base.OnDisappearing(); } } }
動きは以下のような感じです。
(手元にQRコードがなかったのでバーコードを読み取っています。QRコードも同様に読み取る事ができます。)
ViewModelで処理を行う
上のコードは値を読み取った時の処理をコードビハインドに書いています。これをビューモデルに書いてみましょう。
QRScanPageを書き換えて、ビューモデルを追加します。コードは以下の通りです。
QRScanPage.xaml
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="QRCodeStudy.QRScanPage" xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms" xmlns:local="clr-namespace:QRCodeStudy"> <ContentPage.BindingContext> <local:QRScanPageViewModel/> </ContentPage.BindingContext> <ContentPage.Content> <AbsoluteLayout> <Grid AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0.5, 0.5, 1, 1"> <zxing:ZXingScannerView x:Name="zxing" ScanResultCommand="{Binding OnScan}" IsAnalyzing="{Binding IsAnalyzing}"/> <zxing:ZXingDefaultOverlay /> </Grid> <Frame AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0.5, 0.5, AutoSize, AutoSize" IsVisible="{Binding FrameVisible}"> <StackLayout> <Label Text="読み取った値" HorizontalTextAlignment="Center"/> <Label Text="{Binding ScannedCode}" HorizontalTextAlignment="Center"/> </StackLayout> </Frame> </AbsoluteLayout> </ContentPage.Content> </ContentPage>
ZXingScannerViewはScanResultCommandというプロパティを持っているようなのでこれをビューモデルのCommandにバインドしてみます。また、ビューモデルからDisplayAlertを呼び出す方法がわからないので、Frameにスキャン結果を記述するようにします。
QRScanPage.xaml.cs
using Xamarin.Forms; namespace QRCodeStudy { public partial class QRScanPage : ContentPage { public QRScanPage() { InitializeComponent(); } protected override void OnAppearing() { base.OnAppearing(); zxing.IsScanning = true; } protected override void OnDisappearing() { zxing.IsScanning = false; base.OnDisappearing(); } } }
QRScanPageViewModel
using System.ComponentModel; using System.Threading.Tasks; using Xamarin.Forms; namespace QRCodeStudy { public class QRScanPageViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public QRScanPageViewModel() { OnScan = new Command<ZXing.Result>((result)=> { Device.BeginInvokeOnMainThread( async () => { this.IsAnalyzing = false; //読み取り停止 FrameVisible = true; //Frameを表示 ScannedCode = result.Text; await Task.Delay(1000); //1秒待機 this.IsAnalyzing = true; //読み取り再開 FrameVisible = false; //Frameを非表示 }); }); } public Command OnScan { get; } private bool isAnalyzing; public bool IsAnalyzing { get { return isAnalyzing; } set { if (isAnalyzing != value) { isAnalyzing = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAnalyzing))); } } } private string scannedCode; public string ScannedCode { get { return scannedCode; } set { if (scannedCode != value) { scannedCode = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ScannedCode))); } } } private bool frameVisible; public bool FrameVisible { get { return frameVisible; } set { if (frameVisible != value) { frameVisible = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FrameVisible))); } } } } }
コードを読み取ったら読み取った値が記載されているFrameを1秒間表示し、その間は読み取りをしないようにします。Frameが非表示になったら読み取りを再開するようにしています。
動きを見てみましょう。
うまく動きました!
参考にしたページ
ZXing.Net Mobile を使ってみた - Android 編 - - Xamarin 日本語情報
ZXing.Net Mobile を使ってみた - iOS、UWP 編 - - Xamarin 日本語情報
Is there a way to render the Zxing ScannerPage in Xaml? — Xamarin Community Forums