2017/04/28 AndroidのNativeViewの利用について説明追加
Xamarin.Forms2.3.3から追加されたNative Viewsを使用したメモ
この記事はNative Viewsの導入方法を説明する内容ではなく、
導入に際してはまった問題や、ここはまるなーと思ったポイントのメモとなる
導入については下記公式の記事を見るかぴーさんのブログを見るといい
バインディングライブラリについては下記を参照
前提条件
- 母艦はWin10でVisualStudio2015使用(MacBookProも別途利用)
- Xamarin.FormsアプリPCLプロジェクトでiOSとAndroidに対応
- Xamarin.Formsのバージョンは2.3.3
目的
Xamarin.Forms PCLプロジェクトでiOSのNative Views(外部SDK提供物)を利用する
導入理由
サードパーティのObjective-Cライブラリを使用する必要があり、
ライブラリで提供されるネイティブViewの利用が必須だった為
はまりポイント
VS2015ではiOSバインディングプロジェクトのインテリセンスが効かない
NativeViewsは関係なくバインディングプロジェクトの問題
インテリセンスが効かないのはXamarinのバグ仕様である
対処法はプロジェクト参照をやめて作成されたdllをアセンブリ参照すれば良い
詳細な情報が知りたい場合は下記のURLを参照
https://bugzilla.xamarin.com/show_bug.cgi?id=18815
https://forums.xamarin.com/discussion/16164/intellisense-on-projects-referencing-a-binding-project-doesnt-work
PCLの場合NativeViewsはXamlのみ対応(コードビハインドは非対応)
コードビハインドで作成していた場合、Xamlで書き直す必要がある
XamlCompilationOptions.Compileでは動作しない
ビルドエラーも実行時エラーも発生しないで、単純に動かないだけなのでわかりにくい
テンプレートからXamlを作成している場合、初期でCompile設定になっている為、気が付かない恐れがある
アセンブリ名を間違えた場合の例外がわかりにくい
例えばアセンブリ名が「MyApp.iOS」が正解なところ間違えて「MyAppiOS」と入力して実行した場合に発生する例外メッセージは
「MyView not found in xmlns clr-namespace:MyApp.iOS;assembly=MyAppiOS;targetPlatform=iOS
」となり、
アセンブリ名を間違えているのに、あたかもMyAppiOSというアセンブリは存在するが使いたいViewが見つからない印象を与える
xmlns:ios="clr-namespace:MyApp.iOS;assembly=MyAppiOS;targetPlatform=iOS"
<ios:MyView />
プロパティに対するBindingに対応しているが、Getterを作っておかないと実行時例外が発生する
動作する例
<ios:MyView Text="test" />
public class MyView : UIView
{
public string Text { set; }
}
上記のXamlコードを下記のようにBindingに書き直すと動かなくなる
実行時例外が発生するがメッセージは上記アセンブリ名を間違えたときと同じメッセージしか得られない
<ios:MyView Text="{Binding TestText}" />
動作させるにはC#側にgetterを作成する
public class MyView : UIView
{
public string Text { get; set; }
}
Bindingを利用しないで直接代入する場合はGetterがなくても動作するのが厄介
使いたいNativeViewsの初期化に指定されたイニシャライザの利用が必要だった
ライブラリで提供されているViewが既定のコンストラクタを使って初期化すると動かない仕様であった為、単純にXaml側に記載しても動かなかった
具体例を挙げて説明すると
ライブラリで提供されているNativeViewのクラス名が「SdkButton」だった場合
<ios:SdkButton />
と単純に書いても動作しないViewであった
このSdkButtonクラスには静的なメソッドでSdkButtonインスタンスを返すメソッド「ButtonWithType(UIButtonType type)」が用意されており
このメソッドを通してインスタンス化しないと使えないつくりとなっていた
なので下記のようなつくりにする必要があった
<ios:MySdkButton />
public class MySdkButton : UIView
{
private SdkButton _SdkButton;
public MySdkButton()
{
_SdkButton = MySdkButton.ButtonWithType(UIButtonType.RoundedRect);
AddSubview(_SdkButton);
}
}
SdkButtonを直接Xamlで使うのではなく、MySdkButtonというクラスを通して
SdkButtonを表示するようにする
MySdkButtonは内部にSdkButtonをもっておりここで初期化を行う
Viewの初期化に引数が必要(Android)
Androidの場合、Contextを引数に取るものが多い
XamlからContextを渡すには下記のようにする
xmlns:formsAndroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
<droid:MySdkButton x:Arguments="{x:Static formsAndroid:Forms.Context}" />
public class MySdkButton : SdkButton
{
public (Context context) : base(context) { }
}