shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.FormsのShellを一から使ってみる

docs.microsoft.com

環境

Shellってどんなコントロール?

横から出てくるメニューが特徴的なUIコントロール.その他にもサーチバーやタブといった機能が統合されている.

https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/shell/create-images/flyout-reduced.png

それぞれのページが同列の関係のときに使うと良い. 親子関係のような構成の場合,NavigationViewを使うといいと思う.

Shellページの作成

まずはShellページを作るのだけれど,Visual Studioの項目の新規追加ではShellのテンプレートはないので,まずはContent Viewテンプレートを選択して作成する.

f:id:shuhelohelo:20191129011202p:plain

ここではMainShellという名前をつけた.

次にContentPageタグをShellに変更し,ContentPage.Contentなど,中のタグを削除する.

<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="XamarinFormsShellPractice.MainShell">

</Shell>

コードビハインドの修正

MainShell.xaml.csを開くと,MainShellクラスはデフォルトではContentPageを継承している.

これをShellを継承するように変更する.

namespace XamarinFormsShellPractice
{
    public partial class MainShell : Shell
    {
        public MainShell()
        {
            InitializeComponent();
        }
    }
}

アプリケーションのMainPageを変更する

App.xaml.csを開くと,AppクラスのコンストラクタにMainPage = new MainPage();が見つかると思います.

これを以下のようにします.

MainPage = new MainShell();

ここまでで準備が整いました.ソリューションをビルドしましょう.

この時点ではShellの中身が何もないのでデプロイ時にエラーになります.

f:id:shuhelohelo:20191129022323p:plain

Shellの構成

ShellではFlyoutItem, TabBar, Tab, ShellContentからなります.

ShellFlyoutItem, またはTabBarを子要素としてもつことができます.

FlyoutItemはFlyout(ドロワーメニューっていうの?)を使う場合に必要です.

TabBarは下部のタブバーを表します.Flyoutメニューは表示されません.

Tabは下タブバーの一つのタブを表します.FlyoutItemまたはTabBarの中に複数のTabを設けると,下タブが表示されます.

ShellContentをTab内に配置すると上タブが付きます.

あと,MenuItemも置ける.メニューだもんね...

FlyoutItem

Shellの中にFlyoutItem要素を入れると,ハンバーガーメニューや横スワイプでひょっこり出てくるドロワーメニューが表示される.

    <FlyoutItem Title="First">
        <ShellContent>
            <local:MyPage1/>
        </ShellContent>
    </FlyoutItem>
    <FlyoutItem Title="Second">
        <ShellContent>
            <local:MyPage2/>
        </ShellContent>
    </FlyoutItem>

f:id:shuhelohelo:20191129202832p:plain

TabBar(下タブ)

docs.microsoft.com

FlyoutItemの中に複数のTabが存在する場合,下部にタブバーが表示される.ハンバーガーメニューも表示される.ただしFlyoutItemにFlyoutDisplayOptions="AsMultipleItems"をつけておく.こと

これをつけることでFlyoutItem内のそれぞれのTabがFlyoutItemとしてFlyoutに表示されるようになる..これをつけないとFlyout自体は表示されるけれど中身は空.

    <FlyoutItem Title="First" FlyoutDisplayOptions="AsMultipleItems">
        <Tab Title="Tab1">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage1}"/>
        </Tab>
        <Tab Title="Tab2">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage2}"/>
        </Tab>
    </FlyoutItem>

または,各TabをFlyoutItemで囲う.

    <FlyoutItem Title="First">
        <Tab Title="Tab1">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage1}"/>
        </Tab>
    </FlyoutItem>
    <FlyoutItem Title="Second">
        <Tab Title="Tab2">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage2}"/>
        </Tab>
    </FlyoutItem>

TabBarの中にTabを書く.この場合,ハンバーガーメニューは表示されず,Flyoutも出ない.

    <TabBar>
        <Tab Title="Tab1">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage1}"/>
        </Tab>
        <Tab Title="Tab2">
            <ShellContent ContentTemplate="{DataTemplate local:MyPage2}"/>
        </Tab>
    </TabBar>

Flyoutが必要かどうかで使い分けるとよい.

f:id:shuhelohelo:20191129202254p:plain

上記のように<ShellContent ContentTemplate="{DataTemplate local:MyPage1}"/>と記述すると,アプリ起動時にページのインスタンスが生成されるのではなく,ページに遷移するときにインスタンスが作成される.

上タブ

FlyoutItemの中にTabを一つ置いて,その中に複数のShellContentを置くと上部にタブがつく.

複数の ShellContent オブジェクトが Tab にある場合は、上部のタブによってそのオブジェクトをナビゲートできます。
    <FlyoutItem Title="Second">
        <Tab Title="Tab2">
            <ShellContent Title="Tab1" ContentTemplate="{DataTemplate local:MyPage1}"/>
            <ShellContent Title="Tab2" ContentTemplate="{DataTemplate local:MyPage2}"/>
        </Tab>
    </FlyoutItem>

というように、Shellの階層構造は、 FlyoutItem →Tab(下タブ)→上タブ

となっている。

f:id:shuhelohelo:20191129195938p:plain

ドロワーメニューにヘッダーを表示する

Shell.FlyoutHeaderTemplateを使うことで,ヘッダー領域を表示できる. ContentViewの中におしゃれなUIを入れることができる.

    <Shell.FlyoutHeaderTemplate>
        <DataTemplate>
            <ContentView BackgroundColor="Pink" HeightRequest="150">
                <Label Text="Header"
                       TextColor="Black"
                       VerticalOptions="Center"
                       HorizontalOptions="Center"/>
            </ContentView>
        </DataTemplate>
    </Shell.FlyoutHeaderTemplate>

f:id:shuhelohelo:20191129203021p:plain

デフォルトではスワイプ時に変化はしない.

しかし,Shell開始タグ内にFlyoutHeaderBehavior="CollapseOnScroll"を記述することで,上下スワイプ時にサイズを自動で変化させてくれる.

<Shell xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             xmlns:local="clr-namespace:XamarinFormsShellPractice"
             FlyoutHeaderBehavior="CollapseOnScroll"
             x:Class="XamarinFormsShellPractice.MainShell">

上スワイプしたときに縮む↓ f:id:shuhelohelo:20191129203056p:plain

Navigationでのページ遷移先でタブを表示させない

Navigation.PushAsyncでページ遷移したとき,遷移先でもタブが表示される. そして,この遷移先でのタブは動作しない.

遷移先でタブを表示させたくない場合は,以下のように遷移直前に非表示にすることで実現できる.

        private async void Button_Clicked(object sender, EventArgs e)
        {
            var mp2 = new MyPage2();
            Shell.SetTabBarIsVisible(mp2, false);
            await Shell.Current.Navigation.PushAsync(mp2, true);
        }

各ページの生成タイミング

Shell内で切り替えられる各ページはアプリケーション起動時.

これによってパフォーマンスに影響を及ぼすこともある.

必要なときに生成することもできる.

    <FlyoutItem Title="First">
        <ShellContent ContentTemplate="{DataTemplate local:MyPage1}"/>
    </FlyoutItem>
    <FlyoutItem Title="Second">
        <ShellContent ContentTemplate="{DataTemplate local:MyPage2}"/>
    </FlyoutItem>