mediaQuery を使用してアダプティブ レイアウトの情報をクエリする

アプリのレイアウトを更新するには、デバイスの機能やアプリのステータスなど、さまざまな種類の情報が必要です。 ウィンドウの幅と高さは、最も一般的に使用される情報です。この他に、次の情報を参照できます。

  • ウィンドウの姿勢
  • ポインティング デバイスの精度
  • キーボード タイプ
  • デバイスがカメラとマイクをサポートしているかどうか
  • ユーザーとデバイスのディスプレイの間の距離

情報は動的に更新されるため、情報をモニタリングし、更新が発生したときに再コンポーズをトリガーする必要があります。 mediaQuery 関数は、情報検索の詳細を抽象化し、レイアウトの更新をトリガーする条件の定義に集中できるようにします。次の例では、折りたたみ式デバイスの姿勢がテーブルトップの場合に、レイアウトを TabletopLayout に切り替えます。

@Composable
fun VideoPlayer(
    // ...
) {
    // ...
            if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
                TabletopLayout()
            } else {
                FlatLayout()
            }
    // ...
}

mediaQuery 関数を有効にする

mediaQuery 関数を有効にするには、 isMediaQueryIntegrationEnabled 属性を ComposeUiFlags オブジェクトの true に設定します。

class MyApplication : Application() {
    override fun onCreate() {
        ComposeUiFlags.isMediaQueryIntegrationEnabled = true
        super.onCreate()
    }
}

パラメータを使用して条件を定義する

条件は、ラムダ として定義できます。これは、UiMediaScope内で評価されます。 mediaQuery 関数は、現在のステータスとデバイスの機能に応じて条件を評価します。この関数はブール値を返すため、if 式などの条件分岐を使用してレイアウトを決定できます。表 1 に、UiMediaScope で使用できるパラメータを示します。

パラメータ 値の型 説明
windowWidth Dp 現在のウィンドウの幅(dp)。
windowHeight Dp 現在のウィンドウの高さ(dp)。
windowPosture UiMediaScope.Posture アプリケーション ウィンドウの現在の姿勢。
pointerPrecision UiMediaScope.PointerPrecision 使用可能なポインティング デバイスの最高精度。
keyboardKind UiMediaScope.KeyboardKind 使用可能または接続されているキーボードのタイプ。
hasCamera Boolean デバイスでカメラがサポートされているかどうか。
hasMicrophone Boolean デバイスでマイクがサポートされているかどうか。
viewingDistance UiMediaScope.ViewingDistance ユーザーとデバイス画面の間の一般的な距離。

UiMediaScope オブジェクトは、パラメータの値を解決します。mediaQuery 関数は、LocalUiMediaScope.current を使用して、現在のデバイスの機能とコンテキストを表す UiMediaScope オブジェクトにアクセスします。このオブジェクトは、ユーザーがデバイスの姿勢を変更した場合など、変更が行われると動的に更新されます。 次に、mediaQuery 関数は、更新された UiMediaScope オブジェクトを使用して query ラムダを評価し、ブール値を返します。たとえば、次のスニペットでは、windowPosture パラメータの値に基づいて TabletopLayoutFlatLayout を選択します。

@Composable
fun VideoPlayer(
    // ...
) {
    // ...
            if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) {
                TabletopLayout()
            } else {
                FlatLayout()
            }
    // ...
}

ウィンドウ サイズに基づいて決定する

ウィンドウ サイズクラスは、アダプティブ レイアウトの設計、開発、テストに役立つ独自のビューポート ブレークポイントのセットです。現在のウィンドウ サイズを表す 2 つのパラメータを、ウィンドウ サイズクラスで定義されたしきい値と比較できます。次の例では、ウィンドウの幅に応じてペインの数を変更します。 WindowSizeClass クラスには、ウィンドウ サイズクラスのしきい値の定数があります(図 1)。

derivedMediaQuery 関数は query ラムダ を評価し、結果を derivedStateOf でラップします。 windowWidthwindowHeight は頻繁に更新される可能性があるため、query ラムダでこれらのパラメータを参照する場合は、mediaQuery 関数ではなく derivedMediaQuery 関数を呼び出します。

val narrowerThanMedium by derivedMediaQuery {
    windowWidth < WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND.dp
}
val narrowerThanExpanded by derivedMediaQuery {
    windowWidth < WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND.dp
}
when {
    narrowerThanMedium -> SinglePaneLayout()
    narrowerThanExpanded -> TwoPaneLayout()
    else -> ThreePaneLayout()
}

図 1 。レイアウトはウィンドウの幅に応じて更新されます。

ウィンドウの姿勢に応じてレイアウトを更新する

windowPosture パラメータは、現在のウィンドウの姿勢を UiMediaScope.Posture オブジェクトとして記述します。現在の 姿勢 を確認するには、パラメータ を UiMediaScope.Posture クラスで定義されている値と比較します。次の例では、ウィンドウの姿勢に応じてレイアウトを切り替えます。

when {
    mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
}

使用可能なポインティング デバイスの精度を確認する

高精度のポインティング デバイスを使用すると、ユーザーは UI 要素を正確にポイントできます。ポインティング デバイスの精度は、デバイスの種類によって異なります。

pointerPrecision パラメータは、マウスやタッチスクリーンなど、使用可能なポインティング デバイスの精度を表します。UiMediaScope.PointerPrecision クラスには、 FineCoarseBluntNone の 4 つの値が定義されています。 None は、ポインティング デバイスが使用できないことを意味します。 精度は、FineCoarseBlunt の順に高くなります。

複数のポインティング デバイスが使用可能で、精度が異なる場合、パラメータは最も高い精度で解決されます。 たとえば、Fine 精度のデバイスと Blunt 精度のデバイスの 2 つのポインティング デバイスがある場合、pointerPrecision パラメータの値は Fine になります。

次の例では、ユーザーが低精度のポインティング デバイスを使用している場合に、ボタンを大きく表示します。

if (mediaQuery { pointerPrecision == UiMediaScope.PointerPrecision.Blunt }) {
    LargeSizeButton()
} else {
    NormalSizeButton()
}

使用可能なキーボード タイプを確認する

keyboardKind パラメータは、使用可能なキーボードのタイプ( PhysicalVirtualNone)を表します。 画面キーボードが表示され、同時にハードウェア キーボードが使用可能な場合、パラメータは Physical として解決されます。 どちらも検出されない場合、None がパラメータの値になります。 次の例では、キーボードが検出されない場合に、キーボードを接続するよう促すメッセージを表示します。

if (mediaQuery { keyboardKind == UiMediaScope.KeyboardKind.None }) {
    SuggestKeyboardConnect()
}

デバイスがカメラとマイクをサポートしているかどうかを確認する

デバイスによっては、カメラやマイクをサポートしていないものがあります。 デバイスがカメラとマイクをサポートしているかどうかは、hasCamera パラメータと hasMicrophone パラメータで確認できます。 次の例では、デバイスがカメラとマイクをサポートしている場合に、カメラとマイクで使用するボタンを表示します。

Row {
    OutlinedTextField(state = rememberTextFieldState())
    // Show the MicButton when the device supports a microphone.
    if (mediaQuery { hasMicrophone }) {
        MicButton()
    }
    // Show the CameraButton when the device supports a camera.
    if (mediaQuery { hasCamera }) {
        CameraButton()
    }
}

推定される視聴距離で UI を調整する

視聴距離は、レイアウトを決定する要因の一つです。 ユーザーが離れた場所からアプリを使用している場合、テキストと UI 要素が大きくなることが想定されます。viewingDistance パラメータは、デバイスの種類とその一般的な使用状況に基づいて、視聴距離の推定値を提供します。

UiMediaScope.ViewingDistance クラスには、 NearMediumFar の 3 つの値が定義されています。 Near は画面が近いことを意味し、Far はデバイスが離れた場所から見られていることを意味します。次の例では、視聴距離が Far または Medium の場合にフォントサイズを大きくします。

val fontSize = when {
    mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Far } -> 20.sp
    mediaQuery { viewingDistance == UiMediaScope.ViewingDistance.Medium } -> 18.sp
    else -> 16.sp
}

UI コンポーネントをプレビューする

コンポーズ可能な関数で mediaQuery 関数と derivedMediaQuery 関数を呼び出して、UI コンポーネントをプレビューできます。 次のスニペットでは、windowPosture パラメータの値に基づいて TabletopLayoutFlatLayout を選択します。TabletopLayout をプレビューするには、windowPosture パラメータを UiMediaScope.Posture.Tabletop にする必要があります。

when {
    mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
    mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
}

`mediaQuery` 関数と `derivedMediaQuery` 関数は、`LocalUiMediaScope.current` として提供される `UiMediaScope` オブジェクト内で、指定された `query` ラムダを評価します。次の手順でオーバーライドできます。

  1. mediaQuery 関数を有効にします。
  2. UiMediaScope インターフェースを実装するカスタム オブジェクトを定義します。
  3. カスタム オブジェクトを LocalUiMediaScopeCompositionLocalProvider 関数を使用して設定します。
  4. CompositionLocalProvider 関数のコンテンツ ラムダでコンポーズ可能関数を呼び出してプレビューします。

次の例では、TabletopLayout をプレビューできます。

@Preview
@Composable
fun PreviewLayoutForTabletop() {
    // Step 1: Enable the mediaQuery function
    ComposeUiFlags.isMediaQueryIntegrationEnabled = true

    val currentUiMediaScope = LocalUiMediaScope.current
    // Step 2: Define a custom object implementing the UiMediaScope interface.
    // The object overrides the windowPosture parameter.
    // The resolution of the remaining parameters is deferred to the currentUiMediaScope object.
    val uiMediaScope = remember(currentUiMediaScope) {
        object : UiMediaScope by currentUiMediaScope {
            override val windowPosture: UiMediaScope.Posture = UiMediaScope.Posture.Tabletop
        }
    }

    // Step 3: Set the object to the LocalUiMediaScope.
    CompositionLocalProvider(LocalUiMediaScope provides uiMediaScope) {
        // Step 4: Call the composable to preview.
        when {
            mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop } -> TabletopLayout()
            mediaQuery { windowPosture == UiMediaScope.Posture.Book } -> BookLayout()
            mediaQuery { windowPosture == UiMediaScope.Posture.Flat } -> FlatLayout()
        }
    }
}