【Xamarin.Forms】MVVMっぽくBluetoothを実装する(その1:ペアリング済みデバイス一覧を取得する)
タイトル通りです。Xamarin.FormsでBluetoothします。
サマリ
- Xamarin.Formsでペアリング済みデバイスの一覧を取得してListViewに表示した。
- MVVM(っぽく)実装した。
- Androidだけ(iOSは無し/UWPはそもそもやらない)。
- Fody使った!
- 勉強になった!
やること
Xamarin.FormsのAndroidでBluetoothします。 といってもスマホ内のペアリング済みデバイス一覧を取得して情報表示するだけで、通信はしてないですが。
モチベーション
Xamarin.Forms勉強してるけどクラシックBluetoothの実装に関してXamarin.AndroidだったりMVVMっぽく実装してるものがあんまりないっぽかった(コードビハインドにベタ書きしてたり、かのペゾルド本にもVMにロジック書くことを許容するような記述がある)ので自分で作ってみた。 MVVMは知ったかすると宗教戦争の引き金になりそうなので、とりあえずUIとロジックを分離したとかそんな程度です。
ソースコードはgithubにおいてます。 GitHub - johta/XF-BT
動作
ペアリング済みのデバイスとその情報を表示します。
構成
MVVMって単なるフォルダ分けのことではないと思いますが一応Views、ViewModels、Models、Itemsに入れてます。
あと、Fody使うのでFodyWeavers.xmlを作成してプロジェクトに追加してます。 FodyはPropertyChangedにかかわる冗長な記述をシンプルにまとめられるNugetパッケージという認識です。 以下の記事を参考にしつつ、いろいろネットの記事を漁って使い方覚えました。 qiita.com
View
<?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:Views.BTTest" x:Class="BTTest.MainPage"> <StackLayout> <Label Text="{Binding lbl1}"/> <ListView x:Name="listView1" ItemsSource="{Binding ListDevices}" HasUnevenRows="False" RowHeight="80"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout> <Label Text="{Binding Name,StringFormat='名前: {0:T}'}"/> <Label Text="{Binding UUID,StringFormat='UUID: {0:T}'}"/> <Label Text="{Binding Address,StringFormat='MACアドレス: {0:T}'}"/> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>
特に不思議なことは何もしてないけど、ListViewはデフォルトだとLabel
を3行以上表示できない(高さが足りない)ので高さを調節するRowHeight
を設定してます。
using BTTest.ViewModels; using Xamarin.Forms; namespace BTTest { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); BindingContext = new MainPageViewModel(); } } }
本当はコードビハインドには何も書かないほうがいいんだろうなって思いつつ、BindingContext
の設定だけ記述してます。
ViewModel
using BTTest.Items; using BTTest.Models; using PropertyChanged; using System.Collections.ObjectModel; using Xamarin.Forms; namespace BTTest.ViewModels { [AddINotifyPropertyChangedInterface] class MainPageViewModel { public ObservableCollection<BTDevice> ListDevices { get; set; } public BTDevice Device { get; set; } public string lbl1 { get; set; } public MainPageViewModel() { lbl1 = "Bluetoothデバイス一覧"; Initialize(); } public void Initialize() { ListDevices = new ObservableCollection<BTDevice>(); IBluetoothManager btMan = DependencyService.Get<IBluetoothManager>(); var listBTDevices = btMan.GetBondedDevices(); if(listBTDevices != null && listBTDevices.Count > 0 ) { foreach (var device in listBTDevices) { Device = new BTDevice { Name = device.Name, UUID = device.UUID, Address = device.Address }; ListDevices.Add(Device); } } else { lbl1 = "Bluetoothデバイスがありません。"; } } } }
DependencyServiceしてAndroidの固有機能を呼び出して、ペアリング済みデバイスのリストを取得してる。
GetBondedDevice
についてはModelのところで解説。
あと一応、ペアリング済みデバイスがなかったら何もありませんでしたって表示するくらいのif文は実装してる。
Model
using BTTest.Items; using System; using System.Collections.Generic; using System.Text; namespace BTTest.Models { public interface IBluetoothManager { List<BTDevice> GetBondedDevices(); } }
共通プロジェクト(PCLって言うのか?)にはデバイス固有機能の実装を呼び出すためのインターフェースを置く。GetBondedDevice()
って書くだけ。
using Android.Bluetooth; using BTTest.Items; using BTTest.Models; using System.Collections.Generic; using System.Linq; using Xamarin.Forms; [assembly: Dependency(typeof(BTTest.Droid.BluetoothManager))] namespace BTTest.Droid { public class BluetoothManager:IBluetoothManager { private List<BTDevice> result = new List<BTDevice>(); public List<BTDevice> GetBondedDevices() { BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter; //アダプター作成 var listBondedDevices = adapter.BondedDevices; //ペアリング済みデバイスの取得 if(listBondedDevices != null && listBondedDevices.Count>0) { foreach (var device in listBondedDevices) { var btDevice = new BTDevice(); btDevice.Name = device.Name; btDevice.UUID = device.GetUuids().FirstOrDefault().ToString(); btDevice.Address = device.Address; result.Add(btDevice); } } return result; } } }
Androidプロジェクトの方ではBluetoothAPIでペアリング済みデバイスのリストを返すGetBondedDevice
を実装
アダプター作ってペアリング済みデバイスを取得して返してるだけ。
Item
using System; using System.Collections.Generic; using System.Text; namespace BTTest.Items { public class BTDevice { public string Name { get; set; } public string UUID { get; set; } public string Address { get; set; } } }
特に言うことはないが、{ get; set; }の記述を忘れてVMでプロパティーが更新されず、1時間ぐらいwaste of timeしたのは初心者丸出しで恥ずかしすぎて反省。
マニフェストの設定
AndroidでBluetoothするにはマニフェストの設定が必要です。BluetoothとBluetooth_adminにチェックを入れる。
以上だ!
あとは動かせばわかるんじゃないかな。 ちなみにX2Tはイヤホン、8BitDoはゲームコントローラ。
次回はこいつを拡張して実際に接続して値を取得したりしたいなと。