طلب معلومات عن التنسيقات التكيّفية باستخدام 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()
    }
}

تحديد شرط باستخدام مَعلمات

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

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

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

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

تُقيّم الدالة derivedMediaQuery دالة lambda query وتلفّ النتيجة في derivedStateOf. بما أنّ windowWidth وwindowHeight يمكن تحديثهما بشكل متكرر، يُرجى استدعاء الدالة derivedMediaQuery بدلاً من الدالة mediaQuery عند الإشارة إلى هاتين المَعلمتَين في دالة lambda 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. يمكنك التحقّق من الوضع الحالي من خلال مقارنة المَعلمة بالقيم المحدّدة في الفئة UiMediaScope.Posture. يُبدّل المثال التالي التنسيق وفقًا لوضعية النافذة:

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

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

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

تصف المَعلمة pointerPrecision دقة أجهزة التأشير المتاحة، مثل الماوس والشاشة التي تعمل باللمس. هناك أربع قيم محدّدة في الفئة UiMediaScope.PointerPrecision: 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 دالة lambda المحددة query ضمن كائن UiMediaScope، والذي يتم توفيره كـ LocalUiMediaScope.current. يمكنك إلغاء هذا الإعداد من خلال اتّباع الخطوات التالية:

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