الطبقات المعمارية في 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، ما يوفر نظامًا لاختيار المظاهر ومكوناتًا مصمّمة ومؤشرات موجات الانتشار ورموزًا. يمكنك الاستفادة من هذه الطبقة عند استخدام لغة Material Design في تطبيقك.

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

يتمثل المبدأ التوجيهي لواجهة 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 هذا الخيار. في هذه الحالة، يمكنك استخدام عملية تنفيذ Button في Material Design كمرجع وإنشاء المكوّن الخاص بك:

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