Help us understand the problem. What is going on with this article?

Xamarin.FormsでのMasterDetailとバックボタン

More than 3 years have passed since last update.

de:code2017アプリとかで気になってた事があって

MasterDetail組む時にホーム画面への配慮した方が良いかも。っていう問題。

iOSだと意識しなくて良いのだけどAndroidだとバックボタンがあるのでホーム画面に配慮する必要があると思っています。

例えばGoogle MapであったりGmailアプリであったりで、ハンバーガーメニューで他の画面へ移動してからバックスペースを押すと最初の画面へ戻ると思います。

こうなっていないアプリって結構めんどくさくて

例えば設定画面を開いて設定を変更して元の画面に戻る、みたいなケースを想定した時に

ホーム画面を考慮していないアプリの場合

ホーム画面 -> メニューを開く -> 設定画面へ移動 -> 設定操作 -> メニューを開く -> ホームへ戻る

というオペレーションになるのだけど

ホーム画面 -> メニューを開く -> 設定画面へ移動 -> 設定操作 -> バックボタン

っていう操作をしてアプリが閉じちゃう、みたいな事になったりならなかったり。

Xamarin.FormsでCustomRendererを使わずにMasterDetailをホーム画面を意識した感じにはいくつか方法はあるけど、MasterDetailっぽい動作を維持しつつ(開いてるメニューが閉じてDetailが変わる感じ)実現しようとすると、DetailにNavigationPageを二つ重ねる感じで実現できます。

VS2017のXamarinテンプレートのMasterDetailを今回の説明方法で修正したイメージはこんな感じです。

https://sleepyandhungry1984.tumblr.com/post/162319590522/xamarinforms-masterdetail

MasterDetail
Master -- ContentPage
Detai -- NavigationPage(outer)
       ----NavigationPage(inner)
           ----ContentPage(Home)

みたいな感じで構成します。

そして重要な点はinnerナビゲーションの下に追加するページはNavigationPage.SetHasNavigationBarでナビゲーションバーを表示しないようにしておきます。

これでメニューから例えばAboutページを選択した場合にはInnerにPushAsyncして

NavigationPage(outer)
-------NavigationPage(inner)
-------------ContentPage(Home)
-------------ContentPage(About)

という感じにします。

こうするとハンバーガーメニューは残ったままDetailが切り替わります。

でAndroidのバックボタンを押すとAboutがpopしてHomeに戻ると。

Aboutが開いている状態でメニューからHomeを選択した場合にはPopToRootしてあげれば良くて、Aboutが開いている状態で別のメニューを選んだ場合には InnerにPushAsyncした後にAboutをRemovePageする感じでOK。

で、ページ遷移させようとした場合には

NavigationPage(outer)
-------NavigationPage(inner)
-------------ContentPage(Home)
-------ContentPage(Newitem)

という感じで、outerにPushAsyncすることでNavigationBarのハンバーガメニューが ←矢印に変わってナビゲーションする感じです。

メンドクサイし実装が複雑化するしMVVMフレームワークとかだとできなかったりする場合もあるし、Xamarin.Formsでこの辺にコストをかけるのか?っていう疑問もあるんですけど、でも出来てると良いかも?

そんなお話でした。

コードをちょっと張り付けておきます。

#region

using System;
using System.Threading;
using MasterDetail.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

#endregion

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

namespace MasterDetail
{
    // XamlになれるとC#コードでも普通に画面書けることに気が付いた。
    // Intellisenseとコード補完最大限に効くので結構書きやすい。
    // 物凄く読みにくいけどね🍣

    public partial class App : Application
    {
        private readonly NavigationPage _innerNavPage;
        private readonly MasterDetailPage _masterDetailPage;
        private readonly NavigationPage _outerNavPage;

        public App()
        {
            InitializeComponent();

            // browse page
            var browsePage = new ItemsPage
            {
                Title = "Browse"
            };
            NavigationPage.SetHasNavigationBar(browsePage, false);

            // aboutPage
            var aboutPage = new AboutPage
            {
                Title = "About"
            };
            NavigationPage.SetHasNavigationBar(aboutPage, false);

            // InnerNavigationPage
            _innerNavPage = new NavigationPage(browsePage)
            {
                Title = browsePage.Title
            };
            _innerNavPage.PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
                    _innerNavPage.Title = _innerNavPage.CurrentPage.Title;
            };

            // OuterNavigationPage
            _outerNavPage = new NavigationPage(_innerNavPage);

            // MenuPage
            Page menuPage = null;
            menuPage = new ContentPage
            {
                Title = "Menu",
                Content = new StackLayout
                {
                    Children =
                    {
                        new ContentView
                        {
                            Padding = 20,
                            Content = new Label {Text = "Browse"},
                            GestureRecognizers =
                            {
                                new TapGestureRecognizer
                                {
                                    Command = new Command(() => { MessagingCenter.Send(menuPage, "Menu", "Browse"); })
                                }
                            }
                        },
                        new ContentView
                        {
                            Padding = 20,
                            Content = new Label {Text = "About"},
                            GestureRecognizers =
                            {
                                new TapGestureRecognizer
                                {
                                    Command = new Command(() => { MessagingCenter.Send(menuPage, "Menu", "About"); })
                                }
                            }
                        }
                    }
                }
            };


            _masterDetailPage = new MasterDetailPage();
            _masterDetailPage.Master = menuPage;
            _masterDetailPage.Detail = _outerNavPage;

            MainPage = _masterDetailPage;

            MessagingCenter.Subscribe(this, "Navigation",
                new Action<ItemsPage, Page>((s, p) => { _outerNavPage.PushAsync(p); }));

            MessagingCenter.Subscribe(this, "Menu", new Action<Page, string>((p, s) =>
            {
                if (s == "Browse")
                {
                    if (!(_innerNavPage.CurrentPage is ItemsPage))
                        _innerNavPage.PopToRootAsync();
                }
                else if (s == "About")
                {
                    if (_innerNavPage.CurrentPage is AboutPage)
                        return;

                    if (_innerNavPage.CurrentPage is ItemsPage)
                    {
                        _innerNavPage.PushAsync(aboutPage);
                    }
                    else
                    {
                        var currentPage = _innerNavPage.CurrentPage;
                        _innerNavPage.PushAsync(aboutPage)
                            .ContinueWith(task =>
                            {
                                SynchronizationContext.Current.Post(state => { _innerNavPage.Navigation.RemovePage(currentPage); }, null);
                            });
                    }
                }
                _masterDetailPage.IsPresented = false;
            }));
        }
    }
}
yu_ka1984
C#でのクライアント、サーバーサイドの開発してます。Web、リアルタイム、WebAPIなどなど。
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