この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
前回紹介した、Xamarin.Formsのアラートダイアログでは、「Androidのトーストのようなポップアップは無い」と説明しました。 しかし、「絶対にトーストが利用できない」という訳でも有りません。 各プラットフォームにおけるUIルール的に、問題(違和感)無いのかというのは、ここでは一旦棚上げし、Androidのトースト(Android以外ではトーストのようなもの)をXamarin.Formsから利用する要領について説明します。
2 DependencyService
まずは最初に、今回のサンプルで、Xamarin.Formsでトーストを表示している部分のコードを見てください。
App.cs
// ボタンを生成する
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) インターフェース定義(共通プロジェクト)
共通プロジェクトで、インターフェースを定義します。
App.cs
//「依存処理」のインターフェース定義
public interface IMyToast {
void Show(string message);
}
上記では、Show()というメソッドを持つ、IMyToastというインターフェースを定義しました。
(2) インターフェースの実装とDependency指定(各ターゲットごと)
各ターゲットプロジェクトごとにインターフェースの実装を行い、assembly属性でXamarin.Forms.Dependencyを指定します。 なお、属性の記述は、名前空間より外で行う事に注意が必要です。
次のコードは、Androidプロジェクトへ追加したMyToast.csのコードです。 Androidでは、元々「Toast」が有りますので、それをコールしただけです。
MyToast.cs
[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と同じというわけでは有りませんが、頑張れは、見分けがつかないぐらいまで書くことも可能でしょう。 と言うより、完全に「自由に設計できる」とうことです。
Toast.cs
[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>を使用します。
App.cs
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