ピクセルを最大限に活用する - 表示状態の変更に適応する

Windows 8 のアプリは、さまざまな画面サイズとさまざまな表示状態で実行されます。ユーザーは、25 インチのデスクトップ モニターの側端にアプリをスナップすることもできますし、10 インチのワイドスクリーン タブレットの画面全体をアプリで埋めることも可能です。いずれの場合も、皆さんのアプリが利用可能なスペースを十分に活用することが望まれます。この記事では、アプリの現在のサイズと表示状態をコードで追跡する方法と、画面サイズと表示状態の変更を処理するために Windows 8 Consumer Preview でアプリを記述する方法について、ヒントを示します。

//build/ では、さまざまな画面シナリオに合わせてアプリを設計する方法について説明しました。たとえば、XAML に関する講演 (英語) や HTML に関する講演 (英語) をお聞きください。最近の Building Windows 8 ブログでは、画面のスケーリングの調査と設計に関する私たちの考えを少しお伝えしました。多くの場合、明示的なコードを記述しなくても、純粋なマークアップを使って画面サイズの変更に対応できます。しかし、アプリの表示状態 (英語) を追跡し (つまり、アプリが縦長モード、全画面モード、フィル モード、スナップ モードのどれであるか)、それに応じて反応するようにコードを記述することが必要になる場合があります。たとえば、HTML の ListView を使ってアイテムを表示する場合、次の図に示すように全画面モードでは GridLayout を使いますが、スナップ モードでは ListLayout を使います。XAML の場合、GridView コントロールと ListView コントロールを同様に切り替えることができます。このための方法を理解するために、まずはサイズ変更と表示状態の変更をコードで検出する方法を見てみましょう。

全画面表示状態の天気予報アプリでは、3 タイルずつ 3 行のグリッド状に表示される。右のスナップ表示画面では、同じタイルが 1 列に表示される

左の全画面表示状態では、GridLayout (HTML) または GridView コントロール (XAML) が使われている。
右のスナップ表示状態では、ListLayout (HTML) または ListView コントロール (XAML) が使われている。

サイズ変更および表示状態変更の基本

Windows 8 アプリが画面のサイズ変更および表示状態の変更を処理する基本的なパターンは、XAML で記述する場合も HTML で記述する場合も同じです。対応するフレームワークにより用意された該当するイベントにコールバック関数をアタッチし、必要な追加情報をクエリするだけです。

たとえば、JavaScript では、次のコードに示すように、基本的なウィンドウ サイズ変更イベントのハンドラー (これは簡単なコールバック関数です) を作成できます。このイベントを使ってアプリの表示領域がサイズ変更されたことを検出し、アプリ領域の新しい寸法を割り出すことができます。

JavaScript

 function handleResize(eventArgs) {
    var appWidth = eventArgs.view.outerWidth;
    var appHeight = eventArgs.view.outerHeight;
    ...
}

window.addEventListener("resize", handleResize);

同様に、XAML では、現在のウィンドウで SizeChanged イベントを使ってイベント ハンドラーをセットアップできます。

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
    double AppWidth = e.Size.Width;
    double AppHeight = e.Size.Height;
}

Window.Current.SizeChanged += OnWindowSizeChanged;

Windows 8 アプリをプログラミングするときは、WinRT を使って現在の表示状態を直接クエリすることもできます。WinRT には、アプリの現在の表示状態を調べることができる列挙値 (英語) が用意されています。

JavaScript

 var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;

C#

var

  CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;

JavaScript で記述された Windows 8 アプリでは、msMatchMedia 関数もサポートされます。この関数は、DOM ウィンドウ オブジェクトを介して動作し、横長全画面と 1600 ピクセルの画面幅の組み合わせなど、複数のメディア クエリを含む条件がある場合に役立ちます。

JavaScript

 var isSnapped = window.msMatchMedia(“(-ms-view-state:fullscreen-landscape) and 
(min-width: 1600px”).matches;

JavaScript

 function handleSnappedMode(mql) {
if (mql.matches) {
        ...
        }
}
window.msMatchMedia(“(-ms-view-state:fullscreen-landscape) and (min-width: 
1600px”).addListener(handleSnappedMode);

Consumer Preview でのサイズ変更と表示状態

ここまでで、表示状態と画面サイズの情報にアクセスする基本的な方法を確認しました。次に、この機能を展開して画面のサイズ変更を処理する方法について説明します。Windows 8 Consumer Preview では、サイズ変更イベントと表示状態変更イベントの順序は決まっており、表示状態イベント (および関連するコールバック関数) は常にサイズ変更イベントの前に発生します。このため、現在の表示状態にアクセスするには、必ずサイズ変更イベントの実行を待つ必要があります。この結果、統一された簡単なアプローチで、サイズおよび表示状態の変更をコードで処理できます。サイズ変更イベントの実行 (および付随するコールバック関数の呼び出し) を待機することで、現在の表示領域と表示状態に関して返される情報を確実に同期することができます。

さらに、表示状態が変わらない画面サイズ変更イベント (モニター解像度の変更や PC へのリモート接続など) もいくつかあるため、サイズ変更イベントを使って、レイアウト変更を処理するコードもトリガーすることをお勧めします。加えて、画面変更イベントごとに異なるコードを呼び出すのではなく、レイアウト変更を処理するコードを 1 か所にまとめるのも良いプログラミング方法です。

たとえば、現在の画面解像度に応じて異なるサイズの背景画像をダウンロードするアプリを記述する場合を考えてみます。スナップ モードでは、小さい画像をダウンロードして背景にタイル状に表示することができ、フィル モードでは縦横比が標準的な 4:3 の画像、横長全画面モードでは縦横比が 16:9 の画像をダウンロードできます。加えて、画面の高さに応じて、縦横比ごとに異なる画像解像度を選択することができます。

以下の例では、このガイドラインに従って、サイズ変更イベントに基づいてコールバック関数を設定し、このコールバック関数内で WinRT API を使って現在の表示状態をクエリします。

JavaScript

 function handleResize(eventArgs) {
    var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;
    var appHeight = eventArgs.view.outerHeight;
    var appWidth = eventArgs.view.outerWidth;

    // downloadImage requires accurate view state and app size!
    downloadImage(currentViewState, appHeight, appWidth);
}

window.addEventListener("resize", handleResize);

 

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
 var CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value;
 double AppWidth = e.Size.Width;
 double AppHeight = e.Size.Height;

    // DownloadImage requires accurate view state and app size!
    DownloadImage(CurrentViewState, AppHeight, AppWidth);
}

Window.Current.SizeChanged += OnWindowSizeChanged;

一方、表示状態の変更によってトリガーされたコールバック関数でアプリの表示領域サイズをクエリした場合、正しい表示状態情報を取得できますが、アプリ表示領域のサイズは更新されまません。したがって、そのような状況でアプリがスナップ モードから横長全画面モードに移行した場合、AppWidth は 320px となり、表示状態は横長全画面となる可能性があります。これらの値を DownloadImage に渡すと、間違ったサイズの画像がダウンロードされます。

サイズ変更イベントによりトリガーされたコールバックを使う際のこのアドバイスは、他のシナリオにも当てはまります。たとえば、JavaScript で表示状態に応じて ListView コントロールのレイアウトを Grid から List に変更する場合、表示状態が変更されたときにコールバックが発生するように設定できます。しかし、画面がサイズ変更される前に ListView レイアウトを設定すると、ListView コントロールの 2 つのレイアウト (1 つは画面のサイズ変更前、もう 1 つはサイズ変更後) が渡されます (WinJS ListView コントロールはサイズ変更イベントを自動的に処理しますが、表示状態変更イベントは処理しません)。上で考えた DownloadImage の例とは異なり、ユーザーには違いが直接わからないかもしれませんが、アプリのレイアウト処理に余分な時間がかかるため、速度と応答性が低下する可能性があります。したがって、画面のサイズ変更によるコールバックを待機してから、現在の表示状態をクエリし、それに応じて ListView レイアウトを設定することをお勧めします。

JavaScript

 function handleResize(eventArgs) {
    var isSnapped = (Windows.UI.ViewManagement.ApplicationView.value === 
Windows.UI.ViewManagement.ApplicationViewState.snapped);
    listView.layout = isSnapped ? new WinJS.UI.ListLayout() : new WinJS.UI.GridLayout();
}

window.addEventListener("resize", handleResize);

XAML の場合、これらの状態を切り替えるには、VisualStateManager (VSM) (英語) API を使って個々の表示で表示する XAML 要素を定義します。Visual Studio 2011 Beta ツールを使用している場合、グリッド アプリ テンプレート用のプロジェクト テンプレートで定義されたこれらの API の例を参照できます。

XAML

 <VisualStateManager.VisualStateGroups>
    <!-- Visual states reflect the application's view state -->
    <VisualStateGroup>
        <VisualState x:Name="FullScreenLandscape"/>
        <VisualState x:Name="Filled"/>

        <!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
        <VisualState x:Name="FullScreenPortrait">
            <!-- definition of what UI elements should display or change would go here in Storyboard 
elements -->
        </VisualState>

        <!-- The back button and title have different styles when snapped, and the list representation 
is substituted for the grid displayed in all other view states -->
        <VisualState x:Name="Snapped">
            <!-- definition of what UI elements should display or change would go here in Storyboard 
elements -->
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

各種表示状態に定義された VisualState グループに注目してください。この場合、SizeChanged イベントで状態が変更されたときにコードでそれらのグループを変更できます。

C#

 private void OnWindowSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
 // get the view state after the size has changed
 string CurrentViewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString();
 
 // using VisualStateManager make sure we navigate to the correct state definition
    VisualStateManager.GoToState(this, CurrentViewState, false);
}

この例からわかるように、SizeChanged および ApplicationView API と組み合わせて VSM を使うと、画面レイアウトの変更に柔軟に適応できます。

まとめ

この記事では、サイズ変更イベントと表示状態の変更をコードで検出する方法について考えました。Consumer Preview では、ここで行ったように画面サイズ変更イベントを待機してから、表示状態情報を WinRT にクエリすることをお勧めします。このアドバイスに従えば、アプリをサイズ変更するたびに現在の正しい画面サイズと表示状態情報がコードにより検出されます。その他の例については、Windows 8 SDK サンプル (英語) をご覧ください。この記事に関する皆さんのご意見やフィードバックもお待ちしております。

--Windows プログラム マネージャー、Chris Jones

今回の記事作成にあたっては、Tim Heuer 氏にご協力いただきました。ありがとうございました。