使用 mediaQuery 查詢自動調整式版面配置的資訊

您需要各種資訊 (例如裝置功能和應用程式狀態),才能更新應用程式版面配置。視窗寬度和高度是最常用的資訊。此外,您也可以參考下列資訊:

  • 窗戶姿勢
  • 指標裝置精確度
  • 鍵盤類型
  • 裝置是否支援攝影機和麥克風
  • 使用者與裝置螢幕之間的距離

由於資訊會動態更新,因此您需要監控資訊,並在發生任何更新時觸發重組。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 中評估的 lambda。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 lambda,並傳回布林值。舉例來說,下列程式碼片段會根據 windowPosture 參數值,在 TabletopLayoutFlatLayout 之間選擇。

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

根據視窗大小做出決定

視窗大小類別是一組自主的可視區域中斷點,有助於設計、開發及測試自動調整式版面配置。您可以比較代表目前視窗大小的兩個參數,以及視窗大小類別中定義的門檻。以下範例會根據視窗寬度變更窗格數量。WindowSizeClass 類別具有視窗大小類別的閾值常數 (圖 1)。

derivedMediaQuery 函式會評估 query lambda,並將結果包裝在 derivedStateOf 中。由於 windowWidthwindowHeight 可能經常更新,因此在 query lambda 中參照這些參數時,請呼叫 derivedMediaQuery 函式,而不是 mediaQuery 函式。

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 類別中定義了四個值:FineCoarseBluntNoneNone 表示沒有可用的指標裝置。 精確度由高到低依序為:FineCoarseBlunt

如果有多個指標裝置可用,且精確度不同,系統會以精確度最高的裝置解析參數。舉例來說,假設有兩個指標裝置 (Fine 精確裝置和 Blunt 精確裝置),則 FinepointerPrecision 參數的值。

以下範例顯示使用者使用低精確度指標裝置時,按鈕會變大:

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 類別中定義了三個值:NearMediumFarNear 表示螢幕在近距離範圍內,Far 則表示裝置是從遠處觀看。以下範例會在觀看距離為 FarMedium 時,增加字型大小:

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

預覽 UI 元件

您可以在可組合函式中呼叫 mediaQueryderivedMediaQuery 函式,預覽 UI 元件。下列程式碼片段會根據 windowPosture 參數值,選擇 TabletopLayoutFlatLayout。 如要預覽 TabletopLayoutwindowPosture 參數應為 UiMediaScope.Posture.Tabletop

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

mediaQueryderivedMediaQuery 函式會在 UiMediaScope 物件中評估指定的 query lambda,該物件會以 LocalUiMediaScope.current 形式提供。如要覆寫這項設定,請按照下列步驟操作:

  1. 啟用 mediaQuery 函式。
  2. 定義實作 UiMediaScope 介面的自訂物件。
  3. 使用 CompositionLocalProvider 函式,將自訂物件設為 LocalUiMediaScope
  4. CompositionLocalProvider 函式的內容 lambda 中呼叫可組合函式,即可預覽。

您可以透過下列範例預覽 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()
        }
    }
}