الطبقات المعمارية في Jetpack Compose

تقدّم هذه الصفحة نظرة عامة عالية المستوى على الطبقات البنوية التي يتكوّن منها Jetpack Compose، والمبادئ الأساسية التي يستند إليها هذا التصميم.

‫Jetpack Compose ليس مشروعًا واحدًا متكاملاً، بل يتكوّن من عدد من الوحدات التي يتم تجميعها معًا لتشكيل حزمة كاملة. يساعدك فهم الوحدات المختلفة التي يتألف منها Jetpack Compose في:

  • استخدام المستوى المناسب من التجريد لإنشاء تطبيقك أو مكتبتك
  • فهم متى يمكنك "الانتقال" إلى مستوى أدنى لمزيد من التحكّم أو التخصيص
  • تقليل التبعيات

الطبقات

الطبقات الرئيسية في Jetpack Compose هي:

الشكل 1: الطبقات الرئيسية في Jetpack Compose

يتم إنشاء كل طبقة استنادًا إلى المستويات الأدنى، مع دمج الوظائف لإنشاء مكونات ذات مستوى أعلى. تستند كل طبقة إلى واجهات برمجة التطبيقات العامة للطبقات الأدنى للتحقّق من حدود الوحدات وإتاحة إمكانية استبدال أي طبقة عند الحاجة. لنتعرّف على هذه الطبقات من الأسفل إلى الأعلى.

وقت التشغيل
توفّر هذه الوحدة أساسيات وقت تشغيل Compose، مثل remember وmutableStateOf والتعليق التوضيحي @Composable وSideEffect. يمكنك إنشاء واجهة المستخدم مباشرةً على هذه الطبقة إذا كنت بحاجة فقط إلى إمكانات إدارة شجرة Compose، وليس واجهة المستخدم الخاصة بها.
واجهة المستخدم
تتكوّن طبقة واجهة المستخدم من وحدات متعددة ( ui-text، ui-graphics، ui-tooling، وما إلى ذلك). تنفّذ هذه الوحدات أساسيات مجموعة أدوات واجهة المستخدم، مثل LayoutNode، Modifier، ومعالِجات الإدخال، والتنسيقات المخصّصة، والرسم. يمكنك استخدام هذه الطبقة إذا كنت بحاجة فقط إلى المفاهيم الأساسية لمجموعة أدوات واجهة المستخدم.
المكونات الأساسية
توفّر هذه الوحدة وحدات أساسية مستقلة عن نظام التصميم لواجهة مستخدم Compose، مثل Row وColumn وLazyColumn والتعرّف على إيماءات معيّنة وما إلى ذلك. يمكنك إنشاء نظام التصميم الخاص بك استنادًا إلى الطبقة الأساسية.
المادة الأساسية
توفّر هذه الوحدة تنفيذًا لتصميم المتعدد الأبعاد في واجهة مستخدم Compose، وتوفّر نظامًا لتطبيق السمات ومكوّنات منمّقة ومؤشرات تموّج وأيقونات. يمكنك الاستفادة من هذه الطبقة عند استخدام التصميم المتعدد الأبعاد في تطبيقك.

Design principles

من المبادئ التوجيهية في Jetpack Compose توفير أجزاء صغيرة ومحدّدة من الوظائف التي يمكن تجميعها (أو إنشاؤها) معًا، بدلاً من بعض المكوّنات المتكاملة. هذا النهج له عدد من المزايا.

التحكّم

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

على سبيل المثال، إذا أردت تحريك لون مكوّن، يمكنك استخدام واجهة برمجة التطبيقات animateColorAsState:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

ومع ذلك، إذا كنت بحاجة إلى أن يبدأ المكوّن دائمًا باللون الرمادي، فلا يمكنك القيام بذلك باستخدام واجهة برمجة التطبيقات هذه. بدلاً من ذلك، يمكنك استخدام واجهة برمجة التطبيقات Animatable ذات المستوى الأدنى:

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

تعتمد واجهة برمجة التطبيقات animateColorAsState ذات المستوى الأعلى على واجهة برمجة التطبيقات Animatable ذات المستوى الأدنى. يُعدّ استخدام واجهة برمجة التطبيقات ذات المستوى الأدنى أكثر تعقيدًا، ولكنّه يوفّر المزيد من التحكّم. اختَر مستوى التجريد الذي يناسب احتياجاتك على أفضل وجه.

التخصيص

يؤدي تجميع المكوّنات ذات المستوى الأعلى من وحدات البناء الأصغر إلى تسهيل عملية تخصيص المكوّنات بشكل كبير عند الحاجة إلى ذلك. على سبيل المثال، ضع في اعتبارك عملية التنفيذ Button التي توفّرها طبقة التصميم المتعدد الأبعاد:

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

يتم تجميع Button من 4 مكوّنات:

  1. مادة Surface توفّر الخلفية والشكل ومعالجة النقرات وما إلى ذلك

  2. CompositionLocalProvider الذي يغيّر قيمة ألفا للمحتوى عند تفعيل الزر أو إيقافه

  3. ProvideTextStyle يضبط نمط النص التلقائي الذي سيتم استخدامه

  4. Row ويوفر سياسة التنسيق التلقائية لمحتوى الزر

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

ومع ذلك، إذا كنت تريد إجراء تخصيص يتجاوز مَعلمات أحد المكوّنات، يمكنك "النزول" إلى مستوى أدنى وتفريغ مكوّن. على سبيل المثال، يحدّد التصميم المتعدد الأبعاد أنّه يجب أن تكون خلفية الأزرار بلون خالص. إذا كنت بحاجة إلى خلفية متدرّجة الألوان، لا يتيح لك هذا الخيار استخدام المَعلمات Button. في هذه الحالة، يمكنك استخدام تنفيذ التصميم المتعدد الأبعادButton كمرجع وإنشاء المكوّن الخاص بك:

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

يستمر التنفيذ أعلاه في استخدام مكوّنات من طبقة التصميم المتعدد الأبعاد، مثل مفاهيم التصميم المتعدد الأبعاد الخاصة بشفافية المحتوى الحالي ونمط النص الحالي. ومع ذلك، يتم استبدال المادة Surface بالمادة Row وتنسيقها للحصول على المظهر المطلوب.

إذا كنت لا تريد استخدام مفاهيم التصميم المتعدد الأبعاد مطلقًا، مثلاً إذا كنت بصدد إنشاء نظام تصميم مخصّص، يمكنك الانتقال إلى استخدام مكوّنات الطبقة الأساسية فقط:

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

يحتفظ Jetpack Compose بأبسط الأسماء للمكوّنات ذات المستوى الأعلى. على سبيل المثال، androidx.compose.material.Text مبني على androidx.compose.foundation.text.BasicText. يتيح ذلك توفير عملية التنفيذ الخاصة بك باستخدام الاسم الأكثر قابلية للاكتشاف إذا كنت ترغب في استبدال المستويات الأعلى.

اختيار التجريد المناسب

تعتمد فلسفة Compose على إنشاء مكوّنات متعددة الطبقات وقابلة لإعادة الاستخدام، ما يعني أنّه ليس عليك دائمًا استخدام الوحدات الأساسية ذات المستوى الأدنى. لا توفّر العديد من المكوّنات ذات المستوى الأعلى وظائف أكثر فحسب، بل غالبًا ما تطبّق أفضل الممارسات، مثل دعم أدوات تسهيل الاستخدام.

على سبيل المثال، إذا أردت إضافة إمكانية استخدام الإيماءات إلى المكوّن المخصّص، يمكنك إنشاء ذلك من البداية باستخدام Modifier.pointerInput ولكن هناك مكوّنات أخرى ذات مستوى أعلى تم إنشاؤها فوق هذا المكوّن والتي قد توفّر نقطة بداية أفضل، على سبيل المثال Modifier.draggable أو Modifier.scrollable أو Modifier.swipeable.

كقاعدة عامة، يُفضَّل استخدام المكوّن ذي المستوى الأعلى الذي يوفّر الوظائف التي تحتاج إليها للاستفادة من أفضل الممارسات التي يتضمّنها.

مزيد من المعلومات

يمكنك الاطّلاع على نموذج Jetsnack للحصول على مثال على إنشاء نظام تصميم مخصّص.