はじめに
こんにちは、@hiro128_777です。
とうとう iPhone 12 が発表されましたね。でも、Surface Duo も忘れないでください。Surface Duo 日本での発売はまだですが、すでに、エミュレーターで疑似体験することは可能です。
というわけで、10月27日(火)に、エミュレーターで Surface Duo 対応アプリを体験するオンラインハンズオンを開催いたします!
こちらは、簡単な Surface Duo のエミュレーターで動作するマルチスクリーン対応アプリを作って体験していただけるコンテンツになっていますので、ご興味がある方はぜひご参加下さい!
なお、この記事は(2)ですので、最初から手順をお試しになる場合は下記の(1)からお願いします。
hiro128.hatenablog.jp
今回やること
TwoPaneView
を作成し、Surface Duo エミュレータ―上で、Master - Detail 構成のページを Dual Screen 表示できるようにします。
TwoPaneView
はGrid
を継承しており、左右または上下に 2 つのビューを配置できるコンテナーです。
TwoPaneView
の詳細については下記公式ドキュメントをご参照ください。
Dual Screen に対応するには、以下の対応を行うだけであり、非常に簡単です。
- Single Screen 用と、Dual Screen 用の "ガワ" の ContentPage を別々に用意し、それぞれのコンテンツの配置情報のみ記述する。
- 具体的なコンテンツ(配置する UI)の XAML を用意する。
TwoPaneView
が 画面の表示領域のサイズによって、Single Screen 用または、Dual Screen 用 の画面を表示できるように、MinWideModeWidth
をMinTallModeHeight
を設定します。
TwoPaneView
の Single Screen、Dual Screen の制御の詳細については下記公式ドキュメントをご参照ください。
これから作成するサンプルアプリの Single Screen、Dual Screen のときの構成を図に表すと以下のようになります。
Single Screen 時の構成
Dual Screen 時の構成
では、次にコードを書いていきましょう。
TwoPaneView
用のコンテンツページとコンテンツビューのファイル追加する
- View フォルダを追加します。
- 以下の4つのコンテンツページを追加します。
MasterDetailPage.xaml
コンテンツページDetailsPage.xaml
コンテンツページMaster.xaml
コンテンツビューDetails.xaml
コンテンツビュー
以下のようになればOKです。
デフォルトの MainPage に設定されているMainPage.xaml
を削除します。
MasterDetailPage を MainPage として設定します。
App.xaml.cs
public App() { InitializeComponent(); MainPage = new NavigationPage(new MasterDetailPage()); }
MasterDetailPage.xaml のマークアップとコードを記述する
Single Screen 時は Master Page のコンテンツのみ表示し、Dual Screen 時は 左に Master Page のコンテンツ、右に Detail Page のコンテンツを表示します。
MasterDetailPage.xaml
<?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:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:dualScreen="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen" xmlns:local="clr-namespace:XFSurfaceDuoSample2020" mc:Ignorable="d" x:Class="XFSurfaceDuoSample2020.MasterDetailPage"> <dualScreen:TwoPaneView MinWideModeWidth="4000" MinTallModeHeight="4000"> <dualScreen:TwoPaneView.Pane1> <local:Master x:Name="masterPage"></local:Master> </dualScreen:TwoPaneView.Pane1> <dualScreen:TwoPaneView.Pane2> <local:Details x:Name="detailsPage"></local:Details> </dualScreen:TwoPaneView.Pane2> </dualScreen:TwoPaneView> </ContentPage>
MasterDetailPage.xaml.cs
using System.ComponentModel; using System.Linq; using Xamarin.Forms; using Xamarin.Forms.DualScreen; using Xamarin.Forms.Xaml; using XFSurfaceDuoSample2020.Models; using XFSurfaceDuoSample2020.ViewModels; namespace XFSurfaceDuoSample2020 { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class MasterDetailPage { bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane; DetailsPage detailsPagePushed; public MasterDetailPage() { InitializeComponent(); NavigationPage.SetHasNavigationBar(this, IsSpanned); detailsPagePushed = new DetailsPage(); } async void SetupViews() { NavigationPage.SetHasNavigationBar(this, !IsSpanned); NavigationPage.SetHasNavigationBar(detailsPagePushed, !IsSpanned); NavigationPage.SetHasNavigationBar(detailsPage, !IsSpanned); if (!IsSpanned) { if (!Navigation.NavigationStack.Contains(detailsPagePushed)) await Navigation.PushAsync(detailsPagePushed); } } protected override void OnAppearing() { base.OnAppearing(); DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged; } protected override void OnDisappearing() { base.OnDisappearing(); DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged; } void OnFormsWindowPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) || e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape)) { SetupViews(); } } } }
DetailsPage.xaml のマークアップとコードを記述する
Single Screen 時は Detail Page を表示します。Dual Screen 時は使用されません。
DetailsPage.xaml
<?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:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:XFSurfaceDuoSample2020" mc:Ignorable="d" x:Class="XFSurfaceDuoSample2020.DetailsPage" Title="Master Details" Padding="10,0,0,0" > <local:Details></local:Details> </ContentPage>
DetailsPage.xaml.cs
using Xamarin.Forms.DualScreen; using Xamarin.Forms.Xaml; namespace XFSurfaceDuoSample2020 { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class DetailsPage { bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane; public DetailsPage() { InitializeComponent(); } protected override void OnAppearing() { base.OnAppearing(); DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged; } protected override void OnDisappearing() { base.OnDisappearing(); DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged; } async void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if(e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) || e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape)) { if (IsSpanned) await Navigation.PopAsync(); } } } }
Master.xaml のマークアップとコードを記述する
Master Page のコンテンツです。
Master.xaml
<?xml version="1.0" encoding="utf-8" ?> <StackLayout 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="XFSurfaceDuoSample2020.Master" > <StackLayout.Resources> <ResourceDictionary> <Style TargetType="Grid"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="Selected"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="LightSkyBlue" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ResourceDictionary> </StackLayout.Resources> <Picker Title="路線を選択して下さい" Margin="10" HeightRequest="60" FontSize="Large" HorizontalOptions="Fill" VerticalOptions="Fill" ItemsSource="{Binding LineItems}" SelectedItem="{Binding SelectedLineItem, Mode=TwoWay}" > </Picker> <!-- 確認用(後ほど消去する)ここから --> <Label FontSize="Large" TextColor="Coral" Text="Master Page のコンテンツ" HorizontalOptions="CenterAndExpand"> </Label> <!-- 確認用(後ほど消去する)ここまで --> <CollectionView x:Name="Stations" ItemsSource="{Binding StationItems}" SelectionMode="Single" SelectedItem="{Binding SelectedStationItem, Mode=TwoWay}" SelectionChangedCommand="{Binding SelectStationCommand}" > <CollectionView.ItemTemplate> <DataTemplate> <Grid Padding="5" > <Frame Visual="Material" BorderColor="LightGray"> <StackLayout Padding="0" > <Label FontSize="Title" Text="{Binding Name}"> </Label> </StackLayout> </Frame> </Grid> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout>
Master.xaml.cs
using Xamarin.Forms; using Xamarin.Forms.Xaml; using XFSurfaceDuoSample2020.ViewModels; namespace XFSurfaceDuoSample2020 { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class Master : StackLayout { public Master() { InitializeComponent(); } } }
Details.xaml のマークアップとコードを記述する
Detail Page のコンテンツです。
Details.xaml
<?xml version="1.0" encoding="UTF-8"?> <StackLayout 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="XFSurfaceDuoSample2020.Details" BackgroundColor="LightGray" Spacing="0" > <Grid BackgroundColor="White" RowSpacing="2" ColumnSpacing="2" Padding="8,8,8,0" VerticalOptions="Center" > <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Label BackgroundColor="White" HorizontalTextAlignment="End" VerticalTextAlignment="Center" FontSize="Title" Text="ナンバリング :" > </Label> <Label BackgroundColor="White" Grid.Row="1" HorizontalTextAlignment="End" VerticalTextAlignment="Center" FontSize="Title" Text="駅名 :" > </Label> <Label BackgroundColor="White" FontSize="Title" Grid.Column="1" Text="{Binding SelectedStationItem.ID}" VerticalTextAlignment="Center" > </Label> <Label BackgroundColor="White" FontSize="Title" Grid.Column="1" Grid.Row="1" Text="{Binding Name}" VerticalTextAlignment="Center" > </Label> </Grid> <StackLayout BackgroundColor="White" Padding="5"> <!-- 確認用(後ほど消去する)ここから --> <Label FontSize="Large" TextColor="Coral" Text="Detail Page のコンテンツ" HorizontalOptions="CenterAndExpand"> </Label> <!-- 確認用(後ほど消去する)ここまで --> <CollectionView Margin="3" BackgroundColor="LightGray" x:Name="Stations" ItemsSource="{Binding TimeTableRowItems}" SelectionMode="None" > <CollectionView.ItemTemplate> <DataTemplate> <StackLayout BackgroundColor="LightGray" Padding="1"> <Grid BackgroundColor="LightGray" HorizontalOptions="Fill" VerticalOptions="Center" ColumnSpacing="2" Padding="2,1,2,1" > <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="10*"></ColumnDefinition> </Grid.ColumnDefinitions> <Label BackgroundColor="LightSkyBlue" Padding="8" Grid.Column="0" Grid.Row="0" Text="{Binding Hour}" FontSize="Large" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" > </Label> <Label BackgroundColor="Beige" Padding="8" Grid.Column="1" Grid.Row="0" Text="{Binding RowText}" FontSize="Large" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" > </Label> </Grid> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> </StackLayout>
Details.xaml.cs
using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace XFSurfaceDuoSample2020 { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class Details : StackLayout { public Details() { InitializeComponent(); } } }
デバッグ実行し、Single Screen、Dual Screen で表示されるか確認する。
Surface Duo エミュレータ―にデプロイすると、以下のように、Single Screen、Dual Screen で表示が変化します。
Single Screen での表示
Dual Screen での表示
Dual Screen で表示させるためには以下の動画の手順でアプリをスパンしてください。
なお、アプリのロジックはまだ実装していないので、画面が表示されるだけで、UI は反応しません。
Surface Duo Span 操作
youtu.be
続きは以下をご覧ください。
hiro128.hatenablog.jp