[Xamarin.Forms]各プラットフォーム固有機能からFormsで作成したViewを表示する

  • 14
    いいね
  • 0
    コメント

本エントリーは [学生さん・初心者さん大歓迎!]Xamarin Advent Calendar 2016 の 13日目の記事です。

初心者向けの記事ということでXamarin初心者なりに「こういうのってどうやって実装するんだろう」って思ったことについて書かせていただきます。

タイトルに各プラットフォーム固有機能とかありますが 頭蓋骨骨折とかして時間が足りなくて
Androidだけです。折角なのでiOSやUWPも別の機会に書こうとは思います。

以下記事が今回の記事を書こうと思った動機のようなものです。
Xamarin.Formsで通知を出す(Android)(Prism.Forms使用)

また、今回の記事は上記記事同様基本的にPrism.Formsを使用した際の記法で記載致します。

各プラットフォーム固有機能からFormsで作成したViewを表示する

本題です。

Xamrin.Formsでの開発していくにあたり共通部分だけではなく各プラットフォーム固有の実装のため
ネイティブ側をガリガリ書くことも多いと思います。

その際、ネイティブ側からForms側の画面をどうやって呼ぶのだろうと気になったのでとりあえずやってみました。

具体的にはAndroid固有の機能である通知機能を使用してForms側にあるViewを呼んでみようと思います。

Androidの通知タップの遷移先をFormsのViewにする

ページを2つ作成します。
1つは起動時に呼び出されるページで通知を発生させるボタンがあります。
もう1つは発生させた通知をタップすることでのみ遷移できる隠しページという位置づけです。

Forms側の実装

通知を発生させるViewは以下のような最低限の構成の物を作ります。

NotificationPage.xaml
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
      <Button Text="Notification On"
            Command="{ Binding NotificationOnCommand }"
            />
      <Button Text="Notification Off"
              Command="{Binding NotificationOffCommand}"
              />
    </StackLayout>

対応するViewModelです.

NotificationPageViewModel.cs
public class NotificationPageViewModel : BindableBase
    {
        private readonly INotificationOnAndOff _NotificationOnAndOff;
        public DelegateCommand NotificationOnCommand { get; private set; }
        public DelegateCommand NotificationOffCommand { get; private set; }
        public NotificationPageViewModel(INotificationOnAndOff NotificationOnAndOff)
        {
            _NotificationOnAndOff = NotificationOnAndOff;
            NotificationOnCommand = new DelegateCommand(() => _NotificationOnAndOff.NotificationOn());
            NotificationOffCommand = new DelegateCommand(() => _NotificationOnAndOff.NotificationOff());
        }
    }

各プラットフォーム固有機能を呼び出すためのインターフェイスです。

INotificationOnAndOff.cs
public interface INotificationOnAndOff
    {
        void NotificationOn();
        void NotificationOff();
    }

以下が出来上がった画面です。
上のボタンで通知を出し下のボタンで通知が消えます。

2016-12-13.png

続いて隠しページですがこちらもとくになんでもいいので簡単にラベルだけおきます。

HidePage.xaml
<Label Text="(´・ω・`){隠しページだよ" 
       FontSize="64"/>

以上でForms側の設計は終わりです。

最後にApp.xaml.csに忘れずにNavigationPageを登録しておきましょう。

App.xaml.cs
protected override void OnInitialized()
        {
            InitializeComponent();

            NavigationService.NavigateAsync("NavigationPage/NotificationPage/");
        }
protected override void RegisterTypes()
        {
            Container.RegisterTypeForNavigation<NavigationPage>();
            Container.RegisterTypeForNavigation<NotificationPage>();
            // Container.RegisterTypeForNavigation<HidePage>(); いらない
        }

ネイティブ側(Android)の実装

続いて肝心のAndroid側の設計をしていきます。

まずは肝心のネイティブ側からForms側へ遷移するための仕組みを作ります。
Androidプロジェクトの追加からActivityを選択し作成します。

ToHidePageActivity.cs
[Activity(Label = "ToHidePageActivity")]
    public class ToHidePageActivity : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            // Formsに遷移させて
            (Xamarin.Forms.Application.Current.MainPage as NavigationPage).PushAsync(new HidePage());
            // このActivityは自害する
            FinishAndRemoveTask();
        }
    }

HidePageに遷移するためだけのActivityです。
通知から遷移するためにはActivityやServiceが必要みたいなので今回はActivityを使います。

(Xamarin.Forms.Application.Current.MainPage as NavigationPage).PushAsync(new HidePage());

ちょっとわかりずらい感じになっていますがこちらのコードでネイティブ側からForms側の画面遷移を行っています。
Android,Activityだけでしか呼べないといった制約もないようなので基本的にこちらの記述をおこなえばiOS,UWP問わず遷移できるはずだと思います。

次にDependencyServiceやPlatformInitializerなどでインジェクションするために
インターフェイスを継承したクラスを作成します。

container.RegisterType<INotificationOnAndOff, NotificationOnAndOff_Android>(new ContainerControlledLifetimeManager());

PlatformInitializerを使用する場合はMainActivity.csのAndroidInitializerクラスに上記のコードを加えてください。

NotificationOnAndOff_Android.cs
public sealed class NotificationOnAndOff_Android : INotificationOnAndOff
    {
        const int id = 0;

        public void NotificationOn()
        {
            var context = Forms.Context;

            //Forms画面に遷移するためのインテント
            var intent = new Intent(context, typeof(ToHidePageActivity)); 

            var pendingIntent = PendingIntent.GetActivity(context, 0, intent, 0); 

            var n = new Notification.Builder(context)
                    .SetContentTitle("通知")
                    .SetSmallIcon(Resource.Drawable.icon)
                    .SetContentText("タップで隠しページを表示")
                    .SetOngoing(true) 
                    .SetContentIntent(pendingIntent)
                    .Build();

            var nm = (NotificationManager)context.GetSystemService(Context.NotificationService);
            nm.Notify(id, n);
        }

        public void NotificationOff()
        {
            var context = Forms.Context;
            var nm = (NotificationManager)context.GetSystemService(Context.NotificationService);
            nm.Cancel(id);
        }
    }

通知の遷移先のIntentにToHidePageActivityを入れることにより疑似的に通知からの遷移を行っています。

2016-12-13 (1).png

これで上記のような通知が出せるようになりこれをタップすると

2016-12-13 (2).png

Forms側で記述しているがForms側のどこでも呼び出してないHidePageを呼び出すことが出来ました。

以上です。