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