الطبقات المعمارية في 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 للحصول على مثال على إنشاء نظام تصميم مخصّص.