Xamarin.Forms トースト(DependencyService)
1 はじめに
前回紹介した、Xamarin.Formsのアラートダイアログでは、「Androidのトーストのようなポップアップは無い」と説明しました。 しかし、「絶対にトーストが利用できない」という訳でも有りません。 各プラットフォームにおけるUIルール的に、問題(違和感)無いのかというのは、ここでは一旦棚上げし、Androidのトースト(Android以外ではトーストのようなもの)をXamarin.Formsから利用する要領について説明します。
2 DependencyService
まずは最初に、今回のサンプルで、Xamarin.Formsでトーストを表示している部分のコードを見てください。
// ボタンを生成する var button = new Button() { Text = "Toast" }; // ボタンにタッチした際に、インターフェースIMyToastのShow()メソッドを呼ぶ button.Clicked += (s, e) => { DependencyService.Get<IMyToast>().Show("Developers.IO"); };
そして、その実行画面が次のとおりです。
iOS | Android |
Xamarin.Formsの共通コードで、トースト表示のコーディングをすることで、各プラットフォームで動作するようになっています。
これは、Xamarin.Formsにいくつか用意されている拡張方法のうち、「DependencyService」という方法を使用したものです。
Xamarin.Formsには、トーストのUIが用意されていませんので、とりあえず、インターフェース(プロトコル)だけを定義して、それを使用してコーディングします。 そして、その実際の定義は、各プラットフォームのプロジェクトで実装するという訳です。
DependencyServiceを使用する手順は、概ね次のとおりです。
- インターフェース定義(共通プロジェクト)
- インターフェースの実装とDependency指定(各ターゲットごと)
- インスタンスを取得して実行(共通プロジェクト)
(1) インターフェース定義(共通プロジェクト)
共通プロジェクトで、インターフェースを定義します。
//「依存処理」のインターフェース定義 public interface IMyToast { void Show(string message); }
上記では、Show()というメソッドを持つ、IMyToastというインターフェースを定義しました。
(2) インターフェースの実装とDependency指定(各ターゲットごと)
各ターゲットプロジェクトごとにインターフェースの実装を行い、assembly属性でXamarin.Forms.Dependencyを指定します。 なお、属性の記述は、名前空間より外で行う事に注意が必要です。
次のコードは、Androidプロジェクトへ追加したMyToast.csのコードです。 Androidでは、元々「Toast」が有りますので、それをコールしただけです。
[assembly: Xamarin.Forms.Dependency(typeof(MyToast))] namespace App1.Droid { class MyToast:IMyToast { public void Show(string message) { Toast.MakeText(Android.App.Application.Context, message, ToastLength.Short).Show(); } } }
次のコードは、iOSプロジェクトに追加したコードです。 iOSには、Toastが有りませんので、ちょっと厄介です。 無理やり、トーストに似せたUIを作るしかないのです。 具体的には、UIViewを拡張してメッセージを表示し、タイマーでAlpha値を変更することでフェードアウトさせています。
完全に、Androidと同じというわけでは有りませんが、頑張れは、見分けがつかないぐらいまで書くことも可能でしょう。 と言うより、完全に「自由に設計できる」とうことです。
[assembly: Xamarin.Forms.Dependency(typeof(MyToast))] namespace App1.iOS { class MyToast:IMyToast { public void Show(string message) { var toast = new Toast(); toast.Show(UIApplication.SharedApplication.KeyWindow.RootViewController.View, message); } } // 独自のトーストビューを設計する class Toast { // トーストビュー本体 UIView _view; // 文字列を表示するためのラベル UILabel _label; // トーストのサイズ(固定) int _margin = 30; int _height = 40; int _width = 150; NSTimer _timer = null; public Toast() { // トーストビューの生成 _view = new UIView(new CGRect(0, 0, 0, 0)) { BackgroundColor = UIColor.Black, }; _view.Layer.CornerRadius = (nfloat)20.0; // メッセージ表示用のラベル _label = new UILabel(new CGRect(0, 0, 0, 0)) { TextAlignment = UITextAlignment.Center, TextColor = UIColor.White }; _view.AddSubview(_label); } // 表示開始 public void Show(UIView parent, string message) { // 既に表示中の場合は、処理を停止する if (_timer != null) { _timer.Invalidate(); _view.RemoveFromSuperview(); // 親ビューから削除する } // 当初、アルファ値0.7で表示を開始する _view.Alpha = (nfloat)0.7; // 親Viewからトーストのサイズを調整する _view.Frame = new CGRect( (parent.Bounds.Width - _width) / 2, parent.Bounds.Height - _height - _margin, _width, _height); _label.Frame = new CGRect(0, 0, _width, _height); _label.Text = message; // ラベルの表示文字列を設定する parent.AddSubview(_view); //タイマー開始 var wait = 10; // 消え始めるまでのウエイト _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.FromMilliseconds(100), delegate { // alpha値が0になったらViewのサイズを0にしてタイマーを停止する if (_view.Alpha <= 0) { _timer.Invalidate(); _view.RemoveFromSuperview(); // 親ビューから削除する } else { if (wait > 0) { wait--; } else { _view.Alpha -= (nfloat)0.05; } } }); } } }
(3) インスタンスを取得して実行(共通プロジェクト)
共通プロジェクトで、各ターゲットに定義されたクラスのインスタンスを取得するには、DependencyService.Get<T>を使用します。
public class App : Application { public App() { MainPage = new MyPage(); } } class MyPage : ContentPage { public MyPage() { // iOSだけ、上部に余白をとる Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0); // ボタンを生成する var button = new Button() { Text = "Toast" }; // ボタンにタッチした際に、インターフェースIMyToastのShow()メソッドを呼ぶ button.Clicked += (s, e) => { DependencyService.Get<IMyToast>().Show("Developers.IO"); }; // 画面にボタンを配置する Content = new StackLayout { Children = { button } }; } }
3 まとめ
今回は、Xamarin.Formsの拡張方法の1つのDependencyServiceについて紹介しました。
これを見て分かる通り、各プラットフォームでネイティブのフレームワークを利用して拡張できるので、ネイティブで書けることは、ほぼ全部拡張できるということになります。
今回、Windows Phoneのサンプルは有りませんでしたが、要領は同じです。
なお、注意として、Dependencyの定義を忘れたりすると、DependencyService.Get<T>は、インスタンスの取得に失敗してnullを返すので未実装の可能性がある場合は、処置が必要です。
サンプルコード(https://github.com/furuya02/Xamarin.Forms.ToastSample)
本記事は、2015年11月14日現在で最新の Xamarin.Forms 1.5.1.6471 を使用して作成されています。
4 参考リンク
Xamarin Developer Accessing Native Features via the DependencyService