Xamarin.Forms ローカルデータベース(SQLite)の利用
1. ローカルデータベース
Xamarin.Formsでは、SQLite.Netを使用することで、簡単にローカルのSQLiteデータベースが利用可能です。
今回は、このSQLite.NetをPCLで実装する方法について紹介します。
PCLで、このSQLite.Netを利用する場合、プラットフォーム固有の部分(最初の初期化)だけを、DependencyServiceで記述し、それ以外をPCLで実装する形になります。
※プラットフォーム固有の部分とは、主にデータファイルの場所を取得するあたりです。
※Xamarinのページでは、「Portable」と「Sharde」の2種類の実装方法が紹介されています。
2. NuGetパッケージのインストール
SQLite.NetはNuGetパッケージで簡単に利用が可能になっています。
「NuGet パッケージの管理」で 'SQLite.Net.PCL' で検索できます。
最初、うまく「SQLite.Net.Platform.Xamarin(Pratform)」が入らないな・・・と試行錯誤していたら、急にうまく行くようになって、よく見たら最終発行日が昨日になっていたw(途中で更新されたのですね)
※WindowsPhoneの場合は、'SQLite.Net-PCL'の他に依存関係から'sqlite-net-wp8 3.8.5'もインストールされます。
3. インターフェースの定義
プラットフォーム固有の部分をDependencyServiceで記述するためのインターフェースを定義します。
PCLプロジェクトにISQLite.csを追加しました。
これは、SQLiteを初期化して、SQLiteConnectionのインスタンスを取得するものです。
//ISQLite.cs using SQLite.Net; namespace SQLiteSample{ public interface ISQLite{ SQLiteConnection GetConnection(); } }
なお、このインターフェースの利用例は、次のようになります。
db = DependencyService.Get<ISQLite> ().GetConnection (); db.CreateTable<T>();
4. 各プラットフォームの実装
DependencyService用に定義されたインターフェースを各プラットフォームで実装します。iOS
iOSプロジェクトにSQLite_iOS.csを追加しました。using System; using System.IO; using SQLite.Net; using SQLite.Net.Platform.XamarinIOS; using SQLiteSample.iOS; using Xamarin.Forms; [assembly: Dependency(typeof(SQLite_iOS))] namespace SQLiteSample.iOS{ public class SQLite_iOS : ISQLite{ public SQLiteConnection GetConnection(){ const string sqliteFilename = "TodoSQLite.db3"; //データベース名 var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); //Documentsフ ォルダ var libraryPath = Path.Combine(documentsPath, "..", "Library"); // ライブラリフォルダ var path = Path.Combine(libraryPath, sqliteFilename);//DBファイルのパス var plat = new SQLitePlatformIOS(); var conn = new SQLiteConnection(plat, path); return conn; } } }
Android
AndroidプロジェクトにSQLite_Droid.csを追加しました。
using System.IO; using SQLiteSample.Droid; using Xamarin.Forms; [assembly: Dependency(typeof(SQLite_Android))] namespace SQLiteSample.Droid{ public class SQLite_Android : ISQLite{ public SQLite.Net.SQLiteConnection GetConnection() { const string sqliteFilename = "TodoSQLite.db3"; //データベース名 var documentsPath = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal);//Documentsフォルダ var path = Path.Combine(documentsPath, sqliteFilename);//DBファイルのパス var plat = new SQLite.Net.Platform.XamarinAndroid.SQLitePlatformAndroid(); var conn = new SQLite.Net.SQLiteConnection(plat, path); return conn; } } }
WindowsPhone
WindowsPhoneプロジェクトにSQLite_WinPhone.csを追加しました。
using System.IO; using Windows.Storage; using SQLite.Net; using SQLite.Net.Platform.WindowsPhone8; using SQLiteSample.WinPhone; using Xamarin.Forms; [assembly: Dependency(typeof(SQLite_WinPhone))] namespace SQLiteSample.WinPhone { public class SQLite_WinPhone : ISQLite{ public SQLiteConnection GetConnection(){ const string sqliteFilename = "TodoSQLite.db3";//データベース名 var path = Path.Combine(ApplicationData.Current.LocalFolder.Path, sqliteFilename);//DBファイルの パス var plat = new SQLitePlatformWP8(); var conn = new SQLiteConnection(plat, path); return conn; } } }
5. データクラスとデータベースへのアクセスクラスの作成
データクラス
本サンプルのデータクラスとして、PCLプロジェクトにTodoItem.csを追加しました。キーとなるIDのほかに、文字列を保存するText、作成日時のCreatedAt、そして、削除状態を表現するDeleteです。
using System; using SQLite.Net.Attributes; namespace SQLiteSample{ public class TodoItem{ [PrimaryKey, AutoIncrement] public int ID { get; set; } public string Text { get; set; } public DateTime CreatedAt { get; set; }//作成日時 public bool Delete { get; set; }//削除フラグ(trueの時、表示しない) } }
データベースへのアクセスクラス
データベースへのアクセスのための、PCLにTodoRepository.csを定義します。
実装したのは、一覧取得と、データの追加・更新だけです。
using System.Collections.Generic; using SQLite.Net; using Xamarin.Forms; namespace SQLiteSample{ class TodoRepository{ static readonly object Locker = new object(); readonly SQLiteConnection _db; public TodoRepository(){ _db = DependencyService.Get<ISQLite> ().GetConnection ();//データベース接続 _db.CreateTable<TodoItem>();//テーブル作成 } //一覧 public IEnumerable<TodoItem> GetItems(){ lock (Locker){ //Delete==falseの一覧を取得する(削除フラグが立っているものは対象外) return _db.Table<TodoItem>().Where(m => m.Delete == false); } } //更新・追加 public int SaveItem(TodoItem item){ lock (Locker){ if (item.ID != 0){//IDが0で無い場合は、更新 _db.Update(item); return item.ID; } return _db.Insert(item);//追加 } } } }
6. 画面実装とデータベースの利用
以上で、段取りは整ったので、画面とDBアクセスを実装します。
Addボタンで追加、リストのタップで削除になってます。
using System; using Xamarin.Forms; namespace SQLiteSample{ public class App : Application{ public App(){ MainPage = new MyPage(); } protected override void OnStart(){} protected override void OnSleep(){} protected override void OnResume(){} } class MyPage : ContentPage{ readonly TodoRepository _db = new TodoRepository(); public MyPage(){ //データ表示用リストボックス var listView = new ListView{ ItemsSource = _db.GetItems(), ItemTemplate = new DataTemplate(typeof(TextCell)) }; listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Text"); listView.ItemTemplate.SetBinding(TextCell.DetailProperty, new Binding("CreatedAt", stringFormat: "{0:yyy/MM/dd hh:mm}")); listView.ItemTapped += async (s, a) =>{//リストがタップされた時の処理 var item = (TodoItem)a.Item; if (await DisplayAlert("削除してい宜しいですか", item.Text, "OK", "キャンセル")){ item.Delete = true;//削除フラグを有効にして _db.SaveItem(item);//DB更新 listView.ItemsSource = _db.GetItems();//リスト更新 } }; //文字列入力 var entry = new Entry{ HorizontalOptions = LayoutOptions.FillAndExpand }; //追加ボタン var buttonAdd = new Button{ WidthRequest = 60, TextColor = Color.White, Text = "Add" }; buttonAdd.Clicked += (s, a) =>{//追加ボタンが押された時の処理 if (!String.IsNullOrEmpty(entry.Text)){//Entryに文字列が入力されている場合に処理する var item = new TodoItem { Text = entry.Text, CreatedAt = DateTime.Now, Delete = false }; _db.SaveItem(item); listView.ItemsSource = _db.GetItems();//リスト更新 entry.Text = ""; } }; Content = new StackLayout{ Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0), Children = { new StackLayout { BackgroundColor = Color.Navy,//入力部の背景色はネイビー Padding = 5, Orientation = StackOrientation.Horizontal, Children = {entry, buttonAdd}//Entryコントロールとボタンコントロールを横に並べる }, listView//その下にリストボックスを置く } }; } } }
実行画面は次のとおりです。
7. WindowsPhoneの場合
以上で、Xamarin.FormsからSQLite.Netを利用する手順は終了なのですが・・・
WindowsPhoneは、デフォルトでSQLite自体がインストールされていないので、初めてのSQLiteを使用する場合は、このライブラリ自体をインストールする必要があります。そして、アプリ作成時は、このライブラリを参照に追加することになります。
以下、手順を追って説明します。
(1)ダウンロード
http://www.sqlite.org/download.html#wp8から、WindowsPhone8用のものをダウンロードします。(2)インストール
ダウンロードした、vsixファイルを実行して、インストールします。(3)VisualStudio再起動
参照の一覧に表示するためにVisualStudioの再起動が必要です。(4)参照追加
WindowsPhoneプロジェクトの参照の追加で「SQLite for Windows Phone」を追加します。8. 参考にさせて頂いたページ
Xamarin.AndroidでSQLiteを使う - かずきのBlog@hatena
Working with a Local Database - Xamarin
↓サンプルはここに置きました。github.com
Xamarin.Forms 画面のサイズを取得する
コンストラクタでは、サイズが取得できない
次のようなコードを書くと、もれなくサイズの取得に失敗します。単純にContentPageのサイズ(WidthとHeight)を表示しているだけですが、綺麗に-1になってしまっています。
using Xamarin.Forms; namespace App1{ public class App : Application{ public App(){ MainPage = new MyPage(); } protected override void OnStart(){} protected override void OnSleep(){} protected override void OnResume(){} } class MyPage : ContentPage { public MyPage() { Content = new Label{ //ラベル1つ配置する HorizontalOptions = LayoutOptions.Center,//左右中央に配置 VerticalOptions = LayoutOptions.Center,//上下中央に配置 Text = string.Format("Width={0} Height={1}", Width, Height) //ContentPage.Width,ContentPage.Heightプロパティを表示する }; } } }
Android | iOS | WindowsPhone |
-1 * -1 | -1 * -1 | -1 * -1 |
OnSizeAllocated
解決方法の1つとして、ContentPageのサイズが取得できてから、使用するという方法があります。Width,Heightプロパティの値が変化した時点で、OnSizeAllocatedメソッドが呼ばれるので、これをオーバライドする方法です。
まー、この例の場合は、プロパティ値をバインディングした方が手っ取り早いですが・・・
using Xamarin.Forms; namespace App1{ public class App : Application{ public App(){ MainPage = new MyPage(); } protected override void OnStart(){} protected override void OnSleep(){} protected override void OnResume(){} } class MyPage : ContentPage { private Label _label = new Label() { //クラス変数とする HorizontalOptions = LayoutOptions.Center,//左右中央に配置 VerticalOptions = LayoutOptions.Center,//上下中央に配置 }; public MyPage() { Content = _label; //ラベル1つ配置する } protected override void OnSizeAllocated(double width, double height) { base.OnSizeAllocated(width, height); label.Text = string.Format("Width={0} Height={1}", Width, Height);//ContentPage.Width,ContentPage.Heightプロパティを表示する } } }
下のように、サイズが表示されているのが確認できます。
Android | iOS | WindowsPhone |
320 * 496 | 320 * 568 | 480 * 768 |
コンストラクタの前にサイズを確保する
どうしても、コンストラクタの時点でサイズが欲しい場合・・・
それは、もうAppクラスがインスタンス化される前に、確保するしかありません。
思い切って、Appクラスのコンストラクタを引数ありにして、ディスプレイのサイズを取得するように変更します。
※ここで、ターゲットがContentPageのサイズでなく、ディスプレイのサイズになっていることをご了承下さい。
ContentPageが画面いっぱいに構成されているアプリという前提です。
using Xamarin.Forms; namespace App1{ public class App : Application{ public int ScreenWidth { get; private set; } //スクリーンサイズ(幅) public int ScreenHeight { get; private set; }//スクリーンサイズ(高さ) public App(int w,int h){ // パラメータで初期化 ScreenHeight = h; ScreenWidth = w; MainPage = new MyPage(); } protected override void OnStart(){} protected override void OnSleep(){} protected override void OnResume(){} } class MyPage : ContentPage { public MyPage() { Content = new Label{ HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, Text = string.Format("Width={0} Height={1}", ((App)Application.Current).ScreenWidth,((App)Application.Current).ScreenHeight) }; } } }
コンストラクタが引数ありになったので、当然、このままではコンパイルできません。
各プラットフォームで、コンストラクタを生成している部分をいじります。
Android
Androidでは、MainActivity.csを修正します。
Resources.DisplayMetrics.WidthPixels(HeightPixels)でデバイスの実サイズ(dip)を取得し、これをResources.DisplayMetrics.Densityでピクセルに変換しています。
画面サイズを取得して、これをコンストラクタの引数とします。
using Android.App; using Android.Content.PM; using Android.OS; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; namespace App1.Droid{ [Activity(Label = "App1", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : FormsApplicationActivity{ protected override void OnCreate(Bundle bundle){ base.OnCreate(bundle); Forms.Init(this, bundle); //スクリーンのサイズを取得する var w = (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density); var h = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density); LoadApplication(new App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する //LoadApplication(new App());//引数なしはエラーとなる } } }
iOS
iOSでは、AppDelegate.csを修正します。
UIScreen.MainScreen.Bounds.Width(Height)で画面サイズを取得して、これをコンストラクタの引数とします。
using Foundation; using UIKit; namespace App1.iOS{ [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(); //スクリーンのサイズを取得する var w = (int)UIScreen.MainScreen.Bounds.Width; var h = (int)UIScreen.MainScreen.Bounds.Height; LoadApplication(new App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する //LoadApplication(new App()); //引数なしはエラーとなる return base.FinishedLaunching(app, options); } } }
WindowsPhone
WindowsPhoneでは、MainPage.xaml.csを修正します。
Application.Current.Host.Content.ActualWidth(ActualHeight)で画面サイズを取得して、これをコンストラクタの引数とします。
using Microsoft.Phone.Controls; using Xamarin.Forms; using Xamarin.Forms.Platform.WinPhone; using Application = System.Windows.Application; namespace App1.WinPhone{ public partial class MainPage : FormsApplicationPage{ public MainPage(){ InitializeComponent(); SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape; Forms.Init(); //スクリーンのサイズを取得する var w = (int)Application.Current.Host.Content.ActualWidth; var h = (int)Application.Current.Host.Content.ActualHeight; LoadApplication(new App1.App(w,h));//スクリーンサイズをパラメータにしてAppクラスを生成する //LoadApplication(new App1.App());//引数なしはエラーとなる } } }
下のように、サイズが表示されているのが確認できます。
ただし、AndroidとWindowsPhoneでは、高さが微妙に違うので注意が必要です。
対象としている領域が違うのが原因です。
Android | iOS | WindowsPhone |
320 * 521 | 320 * 568 | 480 * 800 |
Xamarin.Forms MasterBehaviorについて
MasterBehavior
MasterBehaviorとは、Xamarin.Forms 1.3で、MasterDetailPageに新しく追加されたプロパティです。
このプロパティは、列挙型になっており、Masterページ(通常メニュー的に左側に表示されるビュー)の隠れ具合を制御しています。
- Split 常時表示
- Popover 隠れる
- SplitOnPortrait 縦の時、常時表示
- SplitOnLandscape 横の時、常時表示
- Default(SplitOnLandscapeと同じ)
なお、このオプションは、iPadなど大きなディスプレイの端末の時だけ機能し、iPhoneの場合は、常に隠れる動作(MasterBehavior.Popoverと同等)となります。
また、Ver1.3では、ちょっとバグがあるような記載があったのVer1.4で動作確認しました。
PM> Get-Package Id Version Description/Release Notes -- ------- ------------------------- WPtoolkit 4.2013.08.16 Windows Phone toolkit provides ... Xamarin.Android.Support.v4 19.0.2 C# bindings for android support library v4. Xamarin.Forms 1.4.2.6355 Build native UIs for iOS, Android, and Windows Phone ..
動作
Popover
メニューが隠れる動作は、つぎのような感じです。Slpit
メニューが常時表示する動作は、つぎのような感じです。コード
使用したコードを参考までにusing System; using System.Linq; using Xamarin.Forms; namespace App1{ public class App : Application{ public App(){ MainPage = new MyPage(); } protected override void OnStart(){} protected override void OnSleep(){} protected override void OnResume(){} } class MyPage : MasterDetailPage{ public MyPage() { MasterBehavior = MasterBehavior.Split; var ar = Enumerable.Range(0, 20).Select(n => "Items-" + n).ToList(); var listView = new ListView { ItemsSource = ar, BackgroundColor = Color.Transparent, }; listView.ItemSelected += (s, a) => SetDetailPage(a.SelectedItem.ToString()); Master = new ContentPage{ BackgroundColor = Color.Teal, Title = "TEST",//Maserページでは、Titleが設定されていないと動作できない Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0), Content = listView, }; SetDetailPage(ar[0]); } void SetDetailPage(string title) { Detail = new NavigationPage(new DetailPage(title)){ BarBackgroundColor = Color.Silver,//上部の背景色 }; try{ //MasterBehavior.Split等でIsPresentedをfalseにしようとすると例外となる IsPresented = false; }catch (Exception ex) { DisplayAlert("Exception",ex.Message, "OK"); } } } class DetailPage : ContentPage{ public DetailPage(string title){ Content = new Label{ Text = title, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center }; } } }
Xamarin.Forms Ver1.3以降の機能について
Ver1.3以降の機能を利用したサンプルアプリ
先日、「第4回 JXUG Conference with dotNetConf 2015 Japan」でお話させて頂いた時の資料です。
昨年末アップデートされた Xamarin 1.3と最近の1.4で利用可能になった新機能を利用して、Twitterクライアント風のアプリを作成してみたので、その説明です。
以下は、サンプルアプリで使用してみた新機能です。
App Lifecycle OnStart,OnSleep,OnResume (1.3)
Properties Dictionary (1.3)
Styles (1.3)
Behaviors (1.3)
Triggers (1.3)
Font (1.3)
ListView context actions (1.3)
pull to refresh (1.4)
headers and footers (1.4)
row separator (1.4)
Scroll (1.3)
ちなみに、使用している翻訳機能「Microsoft Translator 」は、WebAPIですが、言語のFromとToを指定するパラメータがあって、Toだけ指定すると、英語に限らずうまく扱ってくれるようです。
↓サンプルコードはこちらです。github.com
関係ありませんが・・・
知る人ぞ知る「日米商事」ちょっと寄ってきました。
全てのジャンルが揃うわけではありませんが、ここで見つけたら、個別売りでは恐らく最安値では無いでしょうか。
(微妙に、系列の違うものが多数あるので、分かってる人にお勧め)
↓これは、別のお店ですが・・・・思わず買いそうになったアヒル隊長
Xamarin.FormsでWebビューを使用するには?
BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。
Xamarin.FormsでWebビューを使用するには? - Build Insider
使用したコードは、下記にあります。
furuya02/XamarinTips.WebViewSample · GitHub
InfragisticsのXamarin.Forms用のコントロール
Infragistics Xamarin.Forms
現在(2015年4月15日4時)、まだ、正式なアナウンスを見つけられてないのですが・・・予てからアナウンスのあった2015 Volume 1 が出たようです。これにより、InfragisticsのXamarin.Forms用のコントロールも、正式版となります。
InfragisticsのXamarin.Forms用のコントロールは、次の大きく4つのカテゴリで構成されています。
Grids
グリッドは、データバインディングに対応しており、各セルを他のコントロールで自由にデザインできるようです。
また、行のグループ化や、固定行にも対応してるとの事です。
Charts
棒グラフ・円グラフ・分布など色々な表現の可能性を感じます。
Gauges
Gaugeとは、ゲージによる対話型コントロールです。線形・円形などがあります。
Bordodes
そのまんまバーコードです。
プロジェクト作成
テンプレートは用意されていないため、プロジェクトの作成は手動になります。Infragisticsの Xamarin.Formsコントロールを使用するプロジェクトの作成方法は、下記のドキュメントに記載されています。
http://help.infragistics.com/Doc/xamarin/
ドキュメントの中の「Creating Project with Infragistics Controls」を見ると、以下の手順が示されています。
※やや、作業が多そうに見えますが、パッケージ名などが、機能そのままなので、追加するアセンブリもNuGetパッケージも必要なものだけ必要なプロジェクトに入れればいいでしょう。
1. プロジェクトの新規作成
普通に、「Mobile App」-「Blanck App (Xamarin.Forms Portable)」2. 「Java Heap Size」の設定
ヒープのサイズ増加が必要です。3. 参照の追加
インストールされた「C:\Program Files (x86)\Infragistics\2015.1\Xamarin Forms\References」を使用します。
4.NuGetパッケージの追加
インストールされた「C:\Program Files (x86)\Infragistics\2015.1\Xamarin Forms\NuGet」を使用します。5.名前空間の追加
XAML:
xmlns:igCodes="clr-namespace:Infragistics.XF.Controls;assembly=InfragisticsXF.Controls.Barcodes"
xmlns:igCharts="clr-namespace:Infragistics.XF.Controls;assembly=InfragisticsXF.Controls.Charts"
xmlns:igGauges="clr-namespace:Infragistics.XF.Controls;assembly=InfragisticsXF.Controls.Gauges"
xmlns:igGrids="clr-namespace:Infragistics.XF.Controls;assembly=InfragisticsXF.Controls.Grids"
C#:
using Infragistics.XF.Controls;
using Infragistics.XF;
6.ライブラリの初期化コード挿入
Forms.Initの後に、次のコードを挿入する
WinPhone [MainPage.xaml.cs]
Infragistics.XF.Initializers.Barcodes.Init();
Infragistics.XF.Initializers.Charts.Init();
Infragistics.XF.Initializers.Gauges.Init();
Infragistics.XF.Initializers.Grids.Init();
Android [MainActivity.cs]
Infragistics.XF.Initializers.Barcodes.Init();
Infragistics.XF.Initializers.Charts.Init();
Infragistics.XF.Initializers.Gauges.Init();
Infragistics.XF.Initializers.Grids.Init();
iSO [AppDelegate.cs]
Infragistics.XF.Initializers.Barcodes.Init();
Infragistics.XF.Initializers.Charts.Init();
Infragistics.XF.Initializers.Gauges.Init();
Infragistics.XF.Initializers.Grids.Init();
サンプル
少し、書いてみたのですが、ちょっとややこしかったので、取りあえず、サンプル動かしてみました。じっくり読まないと、ちゃっちゃと作れるほど簡単ではないかも・・・
リンク
開発コントロールとデザイン ツール - .Net コンポーネント & コントロール - www.JPInfragistics.com
Developer Controls and Design Tools - .Net Components & Controls - www.Infragistics.com
GrapeCityさんのXuni(ズーニー)も期待です。c1.grapecity.com
Xamarin.Formsでトリガーを使用するには?
BuildInsiderで連載されている「Xamarin逆引きTips」に寄稿させて頂きました。
Xamarin.Formsでトリガーを使用するには? - Build Insider
使用したコードは、下記にあります。
furuya02/XamarinTips.TriggerSample · GitHub