Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
12
Help us understand the problem. What is going on with this article?

More than 3 years have passed since last update.

@aki_lua87

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

本エントリーは [学生さん・初心者さん大歓迎!]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を呼び出すことが出来ました。

以上です。

12
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
12
Help us understand the problem. What is going on with this article?