طلب معلومات عن التنسيقات التكيّفية باستخدام 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 دالة لامدا query باستخدام عنصر UiMediaScope المعدَّل وتعرض قيمة منطقية. على سبيل المثال، يختار المقتطف التالي بين TabletopLayout وFlatLayout استنادًا إلى قيمة المَعلمة windowPosture.

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

اتّخاذ قرار استنادًا إلى حجم النافذة

فئات حجم النافذة هي مجموعة من نقاط توقف إطار العرض المحدّدة مسبقًا التي تساعدك في تصميم تنسيقات سريعة الاستجابة وتطويرها واختبارها. يمكنك مقارنة المَعلمتَين اللتَين تمثّلان حجم النافذة الحالي بالحدّ الذي تم تحديده في فئات حجم النافذة. يغيّر المثال التالي عدد اللوحات وفقًا لعرض النافذة. WindowSizeClass يتضمّن صف ثوابت لحدود فئات حجم النافذة (الشكل 1).

تُقيّم الدالة derivedMediaQuery دالة لامدا query وتغلّف النتيجة في derivedStateOf. بما أنّه يمكن تعديل windowWidth وwindowHeight بشكلٍ متكرّر، استخدِم دالة derivedMediaQuery بدلاً من دالة mediaQuery عند الإشارة إلى هاتَين المَعلمتَين في دالة لامدا query.

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. يمكنك التحقّق من الوضع الحالي posture من خلال مقارنة المَعلمة بالقيم المحدّدة في صف UiMediaScope.Posture. يُبدّل المثال التالي التنسيق وفقًا لوضع النافذة:

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

التحقّق من دقة جهاز التأشير المتاح

يساعد جهاز التأشير عالي الدقة المستخدمين في التأشير على أحد عناصر واجهة المستخدم بدقة. تعتمد دقة جهاز التأشير على نوع الجهاز.

تصف المَعلمة pointerPrecision دقة أجهزة التأشير المتاحة، مثل الماوس والشاشة التي تعمل باللمس. هناك أربع قيم محدّدة في صف UiMediaScope.PointerPrecision class: Fine وCoarse وBlunt وNone. تشير القيمة None إلى عدم توفّر أي جهاز تأشير. تتراوح الدقة من الأعلى إلى الأدنى بهذا الترتيب: Fine وCoarse وBlunt.

إذا كانت هناك أجهزة تأشير متعددة متاحة وكانت دقتها مختلفة، يتم تحديد المَعلمة باستخدام أعلى دقة. على سبيل المثال، إذا كان هناك جهازَا تأشير، أحدهما بدقة Fine والآخر بدقة Blunt، تكون قيمة المَعلمة 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()
    }
}

تعديل واجهة المستخدم باستخدام مسافة المشاهدة المقدَّرة

مسافة المشاهدة هي عامل يساعد في تحديد التنسيق. إذا كان المستخدم يستخدم التطبيق من مسافة بعيدة، سيتوقّع أن يكون النص وعناصر واجهة المستخدم أكبر. تقدّم المَعلمة viewingDistance تقديرًا لمسافة المشاهدة استنادًا إلى نوع الجهاز وسياق استخدامه النموذجي.

هناك ثلاث قيم محدّدة في صف UiMediaScope.ViewingDistance‏: Near وMedium وFar. تشير القيمة Near إلى أنّ الشاشة قريبة، وتشير القيمة Far إلى أنّ الجهاز يتم عرضه من مسافة بعيدة. يزيد المثال التالي حجم الخط عندما تكون مسافة المشاهدة Far أو Medium:

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

معاينة أحد مكوّنات واجهة المستخدم

يمكنك استدعاء الدالتَين mediaQuery وderivedMediaQuery في الدوال المركّبة لمعاينة مكوّنات واجهة المستخدم. يختار المقتطف التالي بين TabletopLayout وFlatLayout استنادًا إلى قيمة المَعلمة windowPosture. لمعاينة 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. يمكنك إلغاء ذلك باتّباع الخطوات التالية:

  1. تفعيل دالة mediaQuery
  2. تحديد عنصر مخصّص ينفّذ واجهة UiMediaScope
  3. ضبط العنصر المخصّص على الـ LocalUiMediaScope باستخدام دالة CompositionLocalProvider
  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()
        }
    }
}