Community Open Day 2012 の補足その4

Community Open Day 2012 の補足その1 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3504
Community Open Day 2012 の補足その2 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3516
Community Open Day 2012 の補足その3 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3526

そんな訳で、デザイン編の続きです。

metro アプリを store に出店させるためには、スナップや回転に対応しないといけない…ってことになっていますが、これが結構面倒、ということを実演します。まぁ、grid を使ってタイリングで配置をさせた場合は、ざっくりと作ればよいのですが、今回のメモ帳のように背景に bitmap が貼りつけてある場合は、大変…ってことなのです。

■スナップや回転は、SizeChanged で検出

iPhone/iPad の場合は、didRotateFromInterfaceOrientation:イベントで回転が検出できるのですが、Windows 8 の場合は、SizeChanged イベントを使います。回転専用のイベントがあってもいい気がするのですが、このイベントは回転だけではなくて、スナップされたときや、スナップから元の全画面に戻ったときも発生する(と思われる)イベントなのです。また、デスクトップPCの場合は、解像度を変更した場合(ディスプレイの接続を変えた場合)にも発生すると考えられます。
なので、サイズイベントなんでしょうねぇ。実はこれが厄介なのですが。

private void Page_Loaded_1(object sender, RoutedEventArgs e)
{
    Window.Current.SizeChanged += Current_SizeChanged;
}

こんな風に SizeChanged イベントを登録しておいて、

private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    var state = Windows.UI.ViewManagement.ApplicationView.Value;
    VisualStateManager.GoToState(this, state.ToString(), false);
}

VisualStateManager の GoToState メソッドを使います。GoToState メソッドは、XAML の VisualStateManager.VisualStateGroups タグのほうに通じています。VisualState タグの Name 属性にマッチするところを探し当てるわけです。
なので、受け渡しする文字列は何でもいいのですが、ApplicationView.Value の値をそのまま文字列に渡してしまうのが楽そうです。

■具体的に回転に対応したレイアウトは?

メモ帳アプリのように、横置き(FullScreenLandscape)と縦置き(FullScreenPortrait)のレイアウトが大きく異なる場合は、結構大変です。
今回の対応を下に出しておくとこれだけあります。

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="FullScreenLandscape"/>
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Storyboard.TargetName="imageBack">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="768"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Height" Storyboard.TargetName="imageBack">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="1024"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Source" Storyboard.TargetName="imageBack">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Images/IMG_0049.PNG"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="listView1">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="imageMask">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Width" Storyboard.TargetName="textBox1">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="658"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Height" Storyboard.TargetName="textBox1">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="856"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="textBox1">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="94,236,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="buttonNew">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="738,152,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="buttonPrev">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="254,1105,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="buttonNext">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="500,1105,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="buttonTrush">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="416,1105,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="buttonContract">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="336,1105,0,0"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

これは、Visual Studio 2012 で提供されているテンプレートをそのまま真似しているので、この方法がベスト…ではないと思いたいのですが、通常の横置きで配置されているコントロールを、ちまちまと Storyboard を使って位置変えをしていきます。Storyboard は、通常はアニメーションをさせるためのタグなのですが、DiscreteObjectKeyFrame KeyTime=”0″ というアニメーション間隔を「0」にするという使い方をすると、位置を変えることと同じことになるのですよ。
なので、縦置き(FullScreenLandscape)で表示されていたコントロールを、横置き(FullScreenPortrait)で座標を指定するという技になります。

が、これは「技」にしか過ぎないので、本来ならばデザイナで表示させたいところですよね。ですが、Visual Studio 2012 RC 版では、この ViewState の部分が「<Base>」しか指定できなくて、手作業で横置きのデザインを作らないと駄目なのです。ここは、製品版で改善されて欲しいところです。この位のコントロールの数ならばよいのですが、沢山ある場合は、デザイナの手に負えないという感じがします。

2012/06/25 追記

もう一度確認したところ、<VisualStateGroup x:Name=”ApplicationViewStates”> のように、ApplicationViewStates を入れると、下記のようにそれぞれの画面を選択できます。

左上のビューのアイコンをクリックするとデザインを切り替えられますね。KeyTime=”0″ のあたりを、デザイナで指定できるかどうかは不明なのですが、これでデザインを確認しながら配置ができます。この話はまた書きます。

■回転アニメーションがない?

あと、これがデザイン的に致命的だと思うのですが、iPad や Android にあるような回転アニメーションがありません。スレートPCを回転させると、パタパタと画面が切り替わるのです。
自前で、回転アニメーションを作ろうと思ってはみたのですが、回転イベント自体が「SizeChanged」という、「回転が終わったときに発生する」イベントなので、metro アプリの回転描画が始まる前ではないのですよ。妙なレイアウトで回転してしまったあとに、SizeChanged イベントが発生するために手が付けようがありませんでした。

このあたりは、背景に bitmap を貼りつけているからだと思います。grid を利用したタイリングの場合には、あまりこのパタパタが気にならないと思います。
そうそう、Windows Phone 7 の場合はどうなんでしょうか?Android も回転アニメーションがあるぐらいなので、あるとは思うのですが実物で試したことがないもので…、この度、.NET ラボあたりで試して貰おう。

ひとまず、Windows Phone 7 の場合は、iPhone 並みに滑らかに回転するそうです。そうなると、Windows 8 metro アプリの場合も綺麗に回転される、というのが想像できそうですね。いまのところ、Acer w500 に windows 8 rp を入れた段階ではバタバタとするので「残念」な気がしていたのですが、製品版にちょっと期待。

カテゴリー: C#, windows 8 パーマリンク