الطبقات المعمارية في 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 ومعالجات الإدخال والتنسيقات المخصّصة والرسم. يمكنك الاستفادة من هذه الطبقة إذا كنت بحاجة فقط إلى المفاهيم الأساسية لمجموعة أدوات واجهة المستخدم.
الأساس
توفّر هذه الوحدة وحدات أساسية لا تعتمد على نظام التصميم لواجهة مستخدم ميزة "الإنشاء"، مثل Row و Column، LazyColumn، وإدراك إيماءات معيّنة، وما إلى ذلك. يمكنك الاستفادة من الطبقة الأساسية لإنشاء نظام التصميم الخاص بك.
المادة الأساسية
توفّر هذه الوحدة تنفيذًا لنظام التصميم المتعدّد الأبعاد ل واجهة مستخدم Compose، ما يوفر نظامًا لاختيار المظاهر ومكوناتًا مصمّمة ومؤشرات موجات الانتشار ورموزًا. ابنِ على هذه الطبقة عند استخدام التصميم متعدد الأبعاد في تطبيقك.

مبادئ التصميم

يتمثل المبدأ التوجيهي لواجهة 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 الذي تقدّمه طبقة Material:

@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 بشأن المعلَمات التي تعرضها، وموازنة ما يؤدي إلى تفعيل التخصيصات الشائعة مقابل مجموعة كبيرة من المعلَمات التي يمكن أن تجعل استخدام أحد المكونات أكثر صعوبة. على سبيل المثال، توفّر مكوّنات Material Design تخصيصات محدّدة في نظام Material Design، ما يسهّل اتّباع مبادئ Material Design.

إذا كنت تريد، مع ذلك، إجراء تخصيص خارج إطار المعلَمات، يمكنك "قائمة منسدلة" لأحد المستويات وعرض أحد المكوّنات. على سبيل المثال، يحدِّد Material Design أنّه يجب أن تكون للأزرار خلفية بلون واحد. إذا كنت تحتاج إلى خلفية متدرّجة، لا تتيح مَعلمات Button هذا الخيار. في هذه الحالة، يمكنك استخدام تطبيق Material 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()
            }
        }
    }
}

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

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

@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 للاطّلاع على مثال على إنشاء نظام تصميم مخصّص.