アプリのレイアウトを更新するには、デバイスの機能やアプリのステータスなど、さまざまな種類の情報が必要です。ウィンドウの幅と高さは、最もよく使用される情報です。また、次の情報も参照してください。
- ウィンドウの姿勢
- ポインティング デバイスの精度
- キーボード タイプ
- カメラとマイクがデバイスでサポートされているかどうか
- ユーザーとデバイスのディスプレイ間の距離
情報は動的に更新されるため、更新が発生したときにモニタリングして再コンポーズをトリガーする必要があります。mediaQuery 関数は情報取得の詳細を抽象化し、レイアウトの更新をトリガーする条件の定義に集中できるようにします。
次の例では、折りたたみ式デバイスの姿勢が卓上モードの場合に、レイアウトを TabletopLayout に切り替えます。
@Composable fun VideoPlayer( // ... ) { // ... if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) { TabletopLayout() } else { FlatLayout() } // ... }
mediaQuery 関数を有効にする
mediaQuery 関数を有効にするには、ComposeUiFlags オブジェクトの isMediaQueryIntegrationEnabled 属性を 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 パラメータの値に基づいて TabletopLayout と FlatLayout のいずれかを選択します。
@Composable fun VideoPlayer( // ... ) { // ... if (mediaQuery { windowPosture == UiMediaScope.Posture.Tabletop }) { TabletopLayout() } else { FlatLayout() } // ... }
ウィンドウ サイズに基づいて決定する
ウィンドウ サイズクラスは、アダプティブ レイアウトの設計、開発、テストに役立つ独自のビューポート ブレークポイントのセットです。現在のウィンドウ サイズを表す 2 つのパラメータを、ウィンドウ サイズクラスで定義されたしきい値と比較できます。次の例では、ウィンドウの幅に応じてペインの数を変更します。WindowSizeClass クラスには、ウィンドウ サイズクラスのしきい値の定数があります(図 1)。
derivedMediaQuery 関数は query ラムダを評価し、結果を derivedStateOf でラップします。windowWidth と windowHeight は頻繁に更新される可能性があるため、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() }
ウィンドウのポーズに応じてレイアウトを更新する
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 クラスには、Fine、Coarse、Blunt、None の 4 つの値が定義されています。None は、ポインティング デバイスが利用できないことを意味します。精度は、Fine、Coarse、Blunt の順に高くなります。
複数のポインティング デバイスが利用可能で、それらの精度が異なる場合、パラメータは最も高い精度で解決されます。たとえば、Fine 精密デバイスと Blunt 精密デバイスの 2 つのポインティング デバイスがある場合、pointerPrecision パラメータの値は Fine です。
次の例は、ユーザーが精度の低いポインティング デバイスを使用している場合に、ボタンを大きく表示する方法を示しています。
if (mediaQuery { pointerPrecision == UiMediaScope.PointerPrecision.Blunt }) { LargeSizeButton() } else { NormalSizeButton() }
利用可能なキーボードの種類を確認する
keyboardKind パラメータは、利用可能なキーボードのタイプ(Physical、Virtual、None)を表します。画面キーボードが表示され、同時にハードウェア キーボードが利用可能な場合、パラメータは 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 クラスには、Near、Medium、Far の 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 パラメータ値に基づいて TabletopLayout と FlatLayout のいずれかを選択します。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 関数は、指定された query ラムダを UiMediaScope オブジェクト内で評価します。このオブジェクトは LocalUiMediaScope.current として提供されます。オーバーライドするには、次の手順を行います。
mediaQuery関数を有効にします。UiMediaScopeインターフェースを実装するカスタム オブジェクトを定義します。CompositionLocalProvider関数を使用して、カスタム オブジェクトをLocalUiMediaScopeに設定します。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() } } }