読者です 読者をやめる 読者になる 読者になる

ぴよぴよエンジニアの日記

ぴよぴよエンジニアの日記です 技術系のことや日常のことをつぶやきます

Xamarin.Forms の Native Embedding を試してみた(失敗談)

Xamarin Android C#

昨日行われた「JXUGC #18 Xamarin.Forms & Prism & Azure Mobile Apps を使いこなそう」で @AyaseSHさん が発表された Native Embedding を試してみました.

eventdots.jp


公式ドキュメントはこちら.

developer.xamarin.com


ぴーさん の素晴らしい解説記事はこちら.

ticktack.hatenablog.jp


これらを参考にしながら Xamarin.Forms PCL プロジェクトで Android.Support.Design.Widget.FloatingActionButton を XAML に配置してみました.


ちなみに、今回のは失敗例で、実用できる状態まで行ってませんw


なお、間違った解釈が多分に含まれている可能性があります.そのときはご指摘いただけますと嬉しいです.



実装環境



Xamarin.Forms 23.3.3.152-pre2 のインストール

XAML にネイティブコントロールの埋め込みができるようになったのは Xamarin.Forms 2.3.3 -pre2 からなのでこれをインストールします.


Prism のテンプレートから Xamarin.Forms を更新すれば普通にいけるかなぁと思ったのですが、更新後 Visual Studio を再起動すると

"パッケージをパス 'Xamarin.Forms.2.3.3.152-pre2\Xamarin.Forms.2.3.3.152-pre2.nupkg'' から読み込めません。
ファイルのデータが壊れています。"

と表示されビルドできなくなったので、おとなしく Xamarin.Forms のアンインストール → 再度インストール という手順を取りました.


Xamarin やってると、ちょっとやそっとのエラーじゃ驚かなくなってきましたねw



XAML

f:id:Santea:20161029173835p:plain

ContentPage に FloatingActionButton を配置した状態です.



<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:androidDesignWidget="clr-namespace:Android.Support.Design.Widget;assembly=Xamarin.Android.Support.Design;targetPlatform=Android"
             xmlns:formsandroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android"
             x:Class="NativeEmbeddingSample2.Views.MainPage"
             Title="MainPage">
  <Grid HorizontalOptions="FillAndExpand"
        VerticalOptions="FillAndExpand">
    
    <androidDesignWidget:FloatingActionButton x:Arguments="{x:Static formsandroid:Forms.Context}"
                                              UseCompatPadding="true"
                                              View.WidthRequest="100"
                                              View.HeightRequest="110"
                                              View.HorizontalOptions="Center"
                                              View.VerticalOptions="Center"/>
  </Grid>
</ContentPage>

FloatingActionButton を配置した XAML です.

要素を1つずつ見ていきます.



<ContentPage xmlns:androidDesignWidget="clr-namespace:Android.Support.Design.Widget;assembly=Xamarin.Android.Support.Design;targetPlatform=Android"
             xmlns:formsandroid="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Platform.Android;targetPlatform=Android" >
</ContentPage>

Android.Widget の assembly が "Mono.Android" だったのに対し、Android.Support.Design.Widget の assembly は Xamarin.Android.Support.Design になります.

ネイティブコントロールの引数用の xmlns:formsandroid は同様です.



<androidDesignWidget:FloatingActionButton x:Arguments="{x:Static formsandroid:Forms.Context}"/>

FloatingActionButton には引数なしコンストラクタが存在しないため、引数を指定します.

おそらく、Android の View 全般にこのことは言えて、引数に Context が必要です.



<androidDesignWidget:FloatingActionButton View.WidthRequest="100"
                                          View.HeightRequest="110"
                                          View.HorizontalOptions="Center"
                                          View.VerticalOptions="Center"/>

FloatingActionButton にも Width/MinimumWidth といったプロパティはありますがことごとく読み取り専用プロパティです.

ネイティブコントロールをラップするViewのパラメータに値やBindingをセットすることも可能です。("View.BackgroundColor=〜〜"の部分) ラッパーである NativeViewWrapper クラスは View のサブクラスなので、指定できるプロパティはViewと同じと考えれば良いでしょう。

ぴーさんが述べられているように、View のプロパティを使って代用します.



<androidDesignWidget:FloatingActionButton UseCompatPadding="true"
                                          View.WidthRequest="100"
                                          View.HeightRequest="110"/>

UseCompatPadding="true" を指定しないと影が見切れてしまいます.

なので true にするのですが、今度は影の分のマージンで横長になってしまうので、Height を大きめにとっています.


ここらへんは Android のお作法だった気がするのですが、正確に覚えてないです.ごめんなさい.

<androidDesignWidget:FloatingActionButton UseCompatPadding="true"
                                          Size="SizeAuto"/>

Xamarin.Android.Design の最新版(24.2.1) では "Size" というプロパティが実装されているので、こんな感じにスマートに書けるようになると思われます.



読み取り専用プロパティの制限が厳しい

さきほどの描画結果で、FAB に画像が付いていなかったことにお気づきでしょうか.

たぶん、XAML 埋め込みで FAB に画像を付けることはできません(泣)

どういうことかというと、FAB の画像は Drawable というプロパティで定義されていますが、こいつは読み取り専用プロパティなんです.

public virtual Drawable Drawable { get; }

通常は SetImageResource(int resId) を使って画像をセットします.

でも!XAML 埋め込みはコードビハインド使えない!!

オワタ\(^o^)/


Android の画像系のビューは全体的にこのような状態に陥ると思います.
(iOS は知らない)


で、Native Embeddingって使えるの?って話ですが、

  • 読み取り専用プロパティの制限が厳しい
  • XAML でインテリセンスが効かない
  • 実行時解釈でデバッグしづらい



という理由で、ぶっちゃけ CustomRenderer で良くね? という結論に落ち着きつつあります.


なにかよっぽど使いやすいネイティブビューでもあれば使いどころになるかもしれません.
(聞きかじった程度ですが、RichTextBox とかは使いやすいのかも?)


私自身、よくわからない部分が多いので、ご意見・ご感想ありましたらぜひお願い致します.




以上です.