ウィンドウ サイズクラス

ウィンドウ サイズクラスは、レスポンシブ/アダプティブ レイアウトの設計、開発、テストに役立つ独自のビューポート ブレークポイントのセットです。ブレークポイントは、レイアウトのシンプルさと、固有のケースに合わせてアプリを最適化する柔軟性のバランスを取ります。

ウィンドウ サイズクラスは、アプリで使用可能な表示領域を「コンパクト」、「中」、または「展開」に分類します。利用可能な幅と高さは別々に分類されているため、どの時点でも、アプリにはウィンドウ サイズクラスが 2 つ(幅用と高さ用)あります。縦方向のスクロールが多いため、通常、利用可能な幅は利用可能な高さよりも重要です。そのため、幅のウィンドウ サイズクラスはアプリの UI により適切であると考えられます。

図 1. 幅に基づくウィンドウ サイズクラスの表現。
図 2. 高さに基づくウィンドウ サイズクラスの表現。

図に示すように、ブレークポイントを使用すると、デバイスと構成の観点からレイアウトについて引き続き検討できます。各サイズクラスのブレークポイントは、典型的なデバイス シナリオの一般的なケースを表しており、ブレークポイントに基づくレイアウトの設計を検討する際の基準として有用です。

サイズクラス ブレークポイント デバイスによる表現
コンパクトな幅 幅 600 dp 未満 縦向きのスマートフォンの 99.96%
中程度の幅 幅 600 dp 以上 840 dp 未満 縦向きのタブレットの 93.73%

広げた状態の最も大きなインナー ディスプレイ(縦向き)

拡大幅 幅 840 dp 以上 横向きのタブレットの 97.22%

展開した状態の最も大きなインナー ディスプレイ(横向き)

コンパクトな高さ 高さ 480 dp 未満 横向きのスマートフォンの 99.78%
中程度の高さ 高さ 480 dp 以上 900 dp 未満 横向きのタブレットの 96.56%

縦向きのスマートフォンの 97.59%

拡大高さ 高さ 900 dp 以上 縦向きのタブレットの 94.25%

サイズクラスを実機として可視化することは便利ですが、ウィンドウ サイズクラスはデバイス画面のサイズによって明示的に決まるわけではありません。ウィンドウ サイズクラスは isTablet タイプのロジック用ではありません。ウィンドウ サイズクラスは、アプリが実行されているデバイスのタイプに関係なく、アプリで使用可能なウィンドウ サイズによって決まります。これには、次の 2 つの重要な影響があります。

  • 物理デバイスで、特定のウィンドウ サイズクラスが保証されるわけではありません。アプリが使用できる画面スペースは、さまざまな理由からデバイスの画面サイズとは異なる場合があります。モバイル デバイスでは、分割画面モードを使用して 2 つのアプリ間で画面を分割できます。ChromeOS では、任意にサイズ変更可能なフリーフォーム ウィンドウで Android アプリを表示できます。折りたたみ式デバイスでは、デバイスを折りたたむか広げることで、2 つの異なるサイズの画面に個別にアクセスできます。

  • ウィンドウ サイズクラスは、アプリの全期間を通じて変化する可能性があります。アプリの実行中に、デバイスの向きの変化、マルチタスク、折りたたみ/展開によって、利用可能な画面スペースの量が変わることがあります。そのため、ウィンドウ サイズクラスは動的であり、アプリの UI はそれに適応する必要があります。

ウィンドウ サイズクラスは、マテリアル デザイン レイアウトのガイダンスに記載されているコンパクト、中程度、拡大の各ブレークポイントにマッピングされます。ウィンドウ サイズクラスを使用して、アプリ レイアウトの概要を決定します。たとえば、特定の正規レイアウトを使用して追加の画面スペースを利用するかどうかを決定します。

現在の WindowSizeClass は、Jetpack WindowManager ライブラリの WindowSizeClass#compute() 関数を使用して計算できます。次の例は、ウィンドウ サイズクラスを計算し、ウィンドウ サイズクラスが変更されるたびに更新を受け取る方法を示しています。

Kotlin

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        val container: ViewGroup = binding.container

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged(). This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged(),
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged() is
        // called in those scenarios.
        container.addView(object : View(this) {
            override fun onConfigurationChanged(newConfig: Configuration?) {
                super.onConfigurationChanged(newConfig)
                computeWindowSizeClasses()
            }
        })

        computeWindowSizeClasses()
    }

    private fun computeWindowSizeClasses() {
        val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
        val width = metrics.bounds.width()
        val height = metrics.bounds.height()
        val density = resources.displayMetrics.density
        val windowSizeClass = WindowSizeClass.compute(width/density, height/density)
        // COMPACT, MEDIUM, or EXPANDED
        val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass
        // COMPACT, MEDIUM, or EXPANDED
        val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Java

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        ViewGroup container = binding.container;

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged(). This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged(),
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged() is
        // called in those scenarios.
        container.addView(new View(this) {
            @Override
            protected void onConfigurationChanged(Configuration newConfig) {
                super.onConfigurationChanged(newConfig);
                computeWindowSizeClasses();
            }
        });

        computeWindowSizeClasses();
    }

    private void computeWindowSizeClasses() {
        WindowMetrics metrics = WindowMetricsCalculator.getOrCreate()
                .computeCurrentWindowMetrics(this);

        int width = metrics.getBounds().width
        int height = metrics.getBounds().height()
        float density = getResources().getDisplayMetrics().density;
        WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density)
        // COMPACT, MEDIUM, or EXPANDED
        WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass()
        // COMPACT, MEDIUM, or EXPANDED
        WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass()

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

ウィンドウ サイズクラスをテストする

レイアウトを変更するときは、すべてのウィンドウ サイズ(特にコンパクト、中程度、拡大のブレークポイント幅)でレイアウトの動作をテストします。

コンパクト画面用の既存のレイアウトがある場合は、まず拡大幅サイズクラスに合わせてレイアウトを最適化します。これは、このサイズクラスが追加のコンテンツと UI の変更に最も大きなスペースを提供するためです。次に、中程度の幅のサイズのクラスに適したレイアウトを決定します。専用のレイアウトの追加を検討してください。

次のステップ

ウィンドウ サイズクラスを使用してレスポンシブ/アダプティブ レイアウトを作成する方法について詳しくは、以下をご覧ください。

すべてのデバイスと画面サイズで優れたアプリを実現する方法の詳細については、以下をご覧ください。