Xamarin.FormsにはQRコードやバーコードを読み取るためのZXing.Net.Mobileというライブラリがあります。
GitHub - Redth/ZXing.Net.Mobile: Zxing Barcode Scanning Library for MonoTouch, Mono for Android, and Windows Phone
今回はこのライブラリを使用して、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"
xmlnsx="http://schemas.microsoft.com/winfx/2009/xaml"
xmlnslocal="clr-namespace:QRCodeStudy"
xClass="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"
xmlnsx="http://schemas.microsoft.com/winfx/2009/xaml"
xClass="QRCodeStudy.QRScanPage"
xmlnszxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms">
<ContentPageContent>
<Grid>
<zxingZXingScannerView xName="zxing"
OnScanResult="Handle_OnScanResult"/>
<zxingZXingDefaultOverlay />
</Grid>
</ContentPageContent>
</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"
xmlnsx="http://schemas.microsoft.com/winfx/2009/xaml"
xClass="QRCodeStudy.QRScanPage"
xmlnszxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlnslocal="clr-namespace:QRCodeStudy">
<ContentPageBindingContext>
<localQRScanPageViewModel/>
</ContentPageBindingContext>
<ContentPageContent>
<AbsoluteLayout>
<Grid AbsoluteLayoutLayoutFlags="All"
AbsoluteLayoutLayoutBounds="0.5, 0.5, 1, 1">
<zxingZXingScannerView xName="zxing"
ScanResultCommand="{Binding OnScan}"
IsAnalyzing="{Binding IsAnalyzing}"/>
<zxingZXingDefaultOverlay />
</Grid>
<Frame AbsoluteLayoutLayoutFlags="PositionProportional"
AbsoluteLayoutLayoutBounds="0.5, 0.5, AutoSize, AutoSize"
IsVisible="{Binding FrameVisible}">
<StackLayout>
<Label Text="読み取った値"
HorizontalTextAlignment="Center"/>
<Label Text="{Binding ScannedCode}"
HorizontalTextAlignment="Center"/>
</StackLayout>
</Frame>
</AbsoluteLayout>
</ContentPageContent>
</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;
ScannedCode = result.Text;
await Task.Delay(1000);
this.IsAnalyzing = true;
FrameVisible = false;
});
});
}
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 日本語情報
GitHub - Redth/ZXing.Net.Mobile: Zxing Barcode Scanning Library for MonoTouch, Mono for Android, and Windows Phone
Is there a way to render the Zxing ScannerPage in Xaml? — Xamarin Community Forums