mediaQuery ile uyarlanabilir düzenler için sorgu bilgileri

Uygulama düzeninizi güncellemek için cihaz kapasitesi ve uygulama durumu gibi çeşitli bilgilere ihtiyacınız vardır. Pencere genişliği ve yüksekliği en çok tercih edilen bilgilerdir. Buna ek olarak, aşağıdaki bilgilere de göz atabilirsiniz:

  • Pencere duruşu
  • İşaretleme cihazlarının hassasiyeti
  • Klavye türü
  • Kamera ve mikrofonun cihaz tarafından desteklenip desteklenmediği
  • Kullanıcı ile cihaz ekranı arasındaki mesafe

Bilgiler dinamik olarak güncellendiği için bunları izlemeniz ve güncelleme olduğunda yeniden oluşturmayı tetiklemeniz gerekir. mediaQuery işlevi, bilgiyi getirme işleminin ayrıntılarını soyutlar ve düzen güncellemelerini tetikleyecek koşulu tanımlamaya odaklanmanızı sağlar.

Aşağıdaki örnekte, katlanabilir cihazın duruşu masaüstü olduğunda düzen TabletopLayout olarak değiştirilir:

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

mediaQuery işlevini etkinleştirme

mediaQuery işlevini etkinleştirmek için ComposeUiFlags nesnesinin isMediaQueryIntegrationEnabled özelliğini true olarak ayarlayın:

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

Parametrelerle koşul tanımlama

Bir koşulu, UiMediaScope içinde değerlendirilen bir lambda olarak tanımlayabilirsiniz. mediaQuery işlevi, koşulu mevcut duruma ve cihaz özelliklerine göre değerlendirir. İşlev, boole değeri döndürür. Bu sayede, if ifadesi gibi koşullu dallarla düzeni belirleyebilirsiniz. Tablo 1'de UiMediaScope içinde kullanılabilen parametreler açıklanmaktadır.

Parametre Değer türü Açıklama
windowWidth Dp dp cinsinden mevcut pencere genişliği.
windowHeight Dp Mevcut pencere yüksekliği (dp cinsinden).
windowPosture UiMediaScope.Posture Uygulama penceresinin mevcut duruşu.
pointerPrecision UiMediaScope.PointerPrecision Mevcut işaretleme cihazları arasında en yüksek hassasiyete sahip olan.
keyboardKind UiMediaScope.KeyboardKind Kullanılabilir veya bağlı klavyenin türü.
hasCamera Boolean Cihazda kameranın desteklenip desteklenmediği.
hasMicrophone Boolean Mikrofonun cihazda desteklenip desteklenmediği.
viewingDistance UiMediaScope.ViewingDistance Kullanıcı ile cihaz ekranı arasındaki ortalama mesafe.

UiMediaScope nesnesi, parametrelerin değerlerini çözer. mediaQuery işlevi, mevcut cihaz özelliklerini ve bağlamını temsil eden UiMediaScope nesnesine erişmek için LocalUiMediaScope.current kullanır. Bu nesne, herhangi bir değişiklik yapıldığında (ör. kullanıcı cihazın duruşunu değiştirdiğinde) dinamik olarak güncellenir. Ardından mediaQuery işlevi, güncellenmiş UiMediaScope nesnesiyle query lambda'yı değerlendirir ve bir boole değeri döndürür. Örneğin, aşağıdaki snippet windowPosture parametre değerine göre TabletopLayout ve FlatLayout arasında seçim yapar.

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

Pencere boyutuna göre karar verme

Pencere boyutu sınıfları, uyarlanabilir düzenler tasarlamanıza, geliştirmenize ve test etmenize yardımcı olan bir dizi özel görüntü alanı kesme noktasıdır. Mevcut pencere boyutunu temsil eden iki parametreyi, pencere boyutu sınıflarında tanımlanan eşikle karşılaştırabilirsiniz. Aşağıdaki örnekte, pencere genişliğine göre bölme sayısı değiştirilmektedir. WindowSizeClass sınıfında, pencere boyutu sınıflarının eşikleri için sabitler bulunur (Şekil 1).

derivedMediaQuery işlevi, query lambda'yı değerlendirir ve sonucu bir derivedStateOf içine sarmalar. windowWidth ve windowHeight sık sık güncellenebileceğinden, query lambda'da bu parametrelere referans verirken mediaQuery işlevi yerine derivedMediaQuery işlevini çağırın.

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()
}

Şekil 1. Düzen, pencere genişliğine göre güncellenir.

Düzeni pencere duruşuna göre güncelleme

windowPosture parametresi, geçerli pencere duruşunu UiMediaScope.Posture nesnesi olarak tanımlar. Parametreyi UiMediaScope.Posture sınıfında tanımlanan değerlerle karşılaştırarak mevcut duruşu kontrol edebilirsiniz. Aşağıdaki örnekte, düzen pencere duruşuna göre değiştirilir:

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

Kullanılabilir işaretleme cihazının hassasiyetini kontrol etme

Yüksek hassasiyetli işaretleme cihazı, kullanıcıların kullanıcı arayüzü öğelerini hassas bir şekilde işaretlemesine yardımcı olur. İşaretleme cihazının hassasiyeti, cihaz türüne bağlıdır.

pointerPrecision parametresi, fare ve dokunmatik ekran gibi kullanılabilir işaretleme cihazlarının hassasiyetini tanımlar. UiMediaScope.PointerPrecision sınıfında tanımlanmış dört değer vardır: Fine, Coarse, Blunt ve None. None, işaretleme cihazı olmadığını gösterir. Hassasiyet, en yüksekten en düşüğe doğru şu şekilde sıralanır: Fine, Coarse ve Blunt.

Birden fazla işaretleme cihazı varsa ve bunların hassasiyetleri farklıysa parametre en yüksek hassasiyete sahip cihazla çözümlenir. Örneğin, iki işaretleme cihazı varsa (biri Fine hassasiyetinde, diğeri Blunt hassasiyetinde) pointerPrecision parametresinin değeri Fine olur.

Aşağıdaki örnekte, kullanıcı düşük hassasiyetli bir işaretleme cihazı kullanırken daha büyük bir düğme gösterilmektedir:

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

Kullanılabilir klavye türünü kontrol etme

keyboardKind parametresi, kullanılabilir klavyelerin türünü temsil eder: Physical, Virtual ve None. Dokunmatik klavye gösteriliyorsa ve aynı anda donanım klavyesi kullanılabiliyorsa parametre Physical olarak çözümlenir. İkisi de algılanmazsa parametrenin değeri None olur. Aşağıdaki örnekte, klavye algılanmadığında kullanıcılara klavye bağlamalarını öneren bir mesaj gösterilmektedir:

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

Cihazın kamera ve mikrofonu destekleyip desteklemediğini kontrol etme

Bazı cihazlar kamera veya mikrofonları desteklemez. Cihazın kamera ve mikrofonu destekleyip desteklemediğini hasCamera parametresi ve hasMicrophone parametresiyle kontrol edebilirsiniz. Aşağıdaki örnekte, cihaz desteklediğinde kamera ve mikrofonla kullanılacak düğmeler gösterilmektedir:

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()
    }
}

Kullanıcı arayüzünü tahmini izleme mesafesine göre ayarlama

Görüntüleme mesafesi, düzeni belirlemeye yardımcı olan bir faktördür. Kullanıcı uygulamayı uzaktan kullanıyorsa metin ve kullanıcı arayüzü öğelerinin daha büyük olmasını bekler. viewingDistance parametresi, cihaz türüne ve tipik kullanım bağlamına göre izleme mesafesinin tahminini sağlar.

UiMediaScope.ViewingDistance sınıfında tanımlanmış üç değer vardır: Near, Medium ve Far. Near, ekranın yakın mesafede olduğu, Far ise cihazın uzaktan görüntülendiği anlamına gelir. Aşağıdaki örnekte, görüntüleme mesafesi Far veya Medium olduğunda yazı tipi boyutu artırılmaktadır:

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

Kullanıcı arayüzü bileşenini önizleme

Kullanıcı arayüzü bileşenlerini önizlemek için composable işlevlerde mediaQuery ve derivedMediaQuery işlevlerini çağırabilirsiniz. Aşağıdaki snippet, windowPosture parametre değerine göre TabletopLayout ve FlatLayout arasında seçim yapar. TabletopLayout önizlemesi için windowPosture parametresi UiMediaScope.Posture.Tabletop olmalıdır.

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

mediaQuery ve derivedMediaQuery işlevleri, LocalUiMediaScope.current olarak sağlanan bir UiMediaScope nesnesi içindeki belirli query lambda'yı değerlendirir. Aşağıdaki adımları uygulayarak bu ayarı geçersiz kılabilirsiniz:

  1. mediaQuery işlevini etkinleştirin.
  2. UiMediaScope arayüzünü uygulayan özel bir nesne tanımlayın.
  3. CompositionLocalProvider işleviyle özel nesneyi LocalUiMediaScope olarak ayarlayın.
  4. CompositionLocalProvider işlevinin içerik lambda'sında önizlemek için composable'ı çağırın.

TabletopLayout için aşağıdaki örneği inceleyebilirsiniz:

@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()
        }
    }
}