الإعدادات التلقائية لواجهة برمجة التطبيقات

تنفّذ واجهات برمجة التطبيقات Material وCompose UI وFoundation العديد من الممارسات التي تسهّل الاستخدام وتوفّرها تلقائيًا. وهي تحتوي على دلالات مضمّنة تتبع دورها ووظيفتها المحددة. هذا يعني أن معظم دعم إمكانية الوصول يتم توفيره مع القليل من العمل الإضافي أو بدونه.

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

يساعدك فهم دلالات وأنماط أدوات تسهيل الاستخدام التلقائية في واجهات برمجة تطبيقات Compose في استخدامها مع مراعاة أدوات تسهيل الاستخدام. كما يساعدك في دعم إمكانية الوصول في المزيد من المكونات المخصصة.

الحدّ الأدنى لأحجام مساحات اللمس

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

تضبط مكوّنات Material، مثل Checkbox وRadioButton وSwitch وSlider وSurface، هذا الحد الأدنى للحجم داخليًا، ولكن فقط عندما يتمكّن المكوّن من تلقّي إجراءات المستخدم. على سبيل المثال، عندما يتم ضبط مَعلمة onCheckedChange في Checkbox على قيمة غير فارغة، يتضمّن مربّع الاختيار مساحة متروكة ليكون عرضه وارتفاعه 48 وحدة بكسل مستقلة الكثافة على الأقل.

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

مربّع اختيار يتضمّن مساحة متروكة تلقائية بعرض وارتفاع 48 بكسل مستقل الكثافة
الشكل 1. مربّع اختيار مع مساحة متروكة تلقائية

عند ضبط المَعلمة onCheckedChange على القيمة null، لا يتم تضمين المساحة المتروكة، لأنّه لا يمكن التفاعل مع المكوّن مباشرةً.

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

مربّع اختيار لا يتضمّن مساحة متروكة
الشكل 2. مربّع اختيار بدون مساحة متروكة

عند تنفيذ عناصر التحكّم في الاختيار، مثل Switch أو RadioButton أو Checkbox، يمكنك عادةً رفع السلوك القابل للنقر إلى حاوية رئيسية من خلال ضبط دالة الاستدعاء للنقر على الدالة المركّبة على null، وإضافة معدِّل toggleable أو selectable إلى الدالة المركّبة الرئيسية.

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

مربّع اختيار بجانب النص "خيار" يتم وضع علامة فيه وإزالتها.
الشكل 3. مربّع اختيار يمكن النقر عليه

عندما يكون حجم دالة مركّبة قابلة للنقر أصغر من الحدّ الأدنى لحجم مساحة اللمس، تزيد أداة Compose حجم مساحة اللمس. ويتم ذلك من خلال توسيع حجم مساحة اللمس خارج حدود الدالة المركّبة.

يحتوي المثال التالي على عنصر Box صغير جدًا يمكن النقر عليه. يتم توسيع مساحة اللمس تلقائيًا لتتجاوز حدود "Box"، لذا فإنّ النقر بجانب "Box" سيؤدي إلى بدء الحدث الناتج عن النقر.

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

مربّع صغير جدًا قابل للنقر يتم توسيعه إلى مساحة لمس أكبر من خلال النقر بجانب المربّع
الشكل 4. مربّع صغير جدًا قابل للنقر يتم توسيعه إلى مساحة لمس أكبر

لمنع التداخل المحتمل بين مناطق اللمس في الدوال المركّبة المختلفة، استخدِم دائمًا حدًا أدنى للحجم كبيرًا بما يكفي للدالة المركّبة. في المثال، يعني ذلك استخدام المُعدِّل sizeIn لضبط الحد الأدنى لحجم المربّع الداخلي:

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

تمت زيادة حجم المربع الصغير جدًا من المثال السابق لإنشاء مساحة لمس أكبر.
الشكل 5. مساحة لمس أكبر للمربّع

إذا كان يجب أن يحتفظ العنصر بحجم مرئي أصغر، استخدِم المعدِّل minimumInteractiveComponentSize من Material 3 بدلاً من ذلك. فهو يحجز ما لا يقل عن 48 بكسل مستقل الكثافة من مساحة التخطيط في كل بُعد ويضع العنصر في منتصف تلك المساحة، بحيث يحتفظ العنصر بحجمه المرسوم وتتحرك العناصر المحيطة به لإفساح المجال. لا يوسّع المُعدِّل مساحة اللمس نفسها، بل يترك مساحة في التنسيق للتوسيع الموضّح سابقًا. تستخدم مكوّنات Material، مثل Checkbox، هذا المُعدِّل داخليًا. يجب وضعه قبل أي عنصر تعديل حجم، لأنّ عنصر تعديل الحجم الذي يسبقه يحدّ من المساحة التي يمكنه حجزها.

@Composable
private fun MinimumInteractiveBox() {
    var clicked by remember { mutableStateOf(false) }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Box(
            Modifier
                .clickable { clicked = !clicked }
                .minimumInteractiveComponentSize()
                .size(24.dp)
                .background(if (clicked) Color.DarkGray else Color.Black)
        )
        Text("Option")
    }
}

العناصر الرسومية

عند تحديد دالة مركّبة Image أو Icon، لا تتوفّر طريقة تلقائية لإطار عمل Android لفهم ما يعرضه التطبيق. يجب تمرير وصف نصي للعنصر الرسومي.

تخيَّل شاشة يمكن للمستخدم من خلالها مشاركة الصفحة الحالية مع الأصدقاء. تحتوي هذه الشاشة على رمز مشاركة يمكن النقر عليه:

شريط يضم أربعة رموز يمكن النقر عليها، مع تمييز رمز "المشاركة"
الشكل 6. صف من الرموز القابلة للنقر مع اختيار الرمز "مشاركة"

استنادًا إلى الرمز وحده، لا يمكن لإطار عمل Android وصفه لمستخدم يعاني من ضعف البصر. يحتاج إطار عمل Android إلى وصف نصي إضافي للرمز.

تصف المَعلمة contentDescription عنصرًا رسوميًا. استخدِم سلسلة مترجَمة، لأنّها تظهر للمستخدم.

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

بعض العناصر الرسومية تكون زخرفية بحتة وقد لا ترغب في إبلاغ المستخدم بها. عند ضبط المَعلمة contentDescription على null، يشير ذلك إلى إطار عمل Android بأنّ هذا العنصر لا يتضمّن إجراءات أو حالة مرتبطة به.

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

contentDescription مخصّص بشكل أساسي للاستخدام مع العناصر الرسومية، مثل الصور. تتضمّن مكوّنات Material، مثل Button أو Text، والسلوكيات القابلة للتنفيذ، مثل clickable أو toggleable، دلالات أخرى محدّدة مسبقًا تصف سلوكها الجوهري، ويمكن تغييرها من خلال واجهات برمجة تطبيقات Compose الأخرى.

عناصر تفاعلية

تنشئ واجهات برمجة التطبيقات Material وFoundation Compose عناصر في واجهة المستخدم يمكن للمستخدمين التفاعل معها من خلال واجهات برمجة التطبيقات المعدِّلة clickable وtoggleable. بما أنّ المكوّنات القابلة للتفاعل قد تتألف من عناصر متعددة، يدمج clickable وtoggleable دلالات العناصر الثانوية تلقائيًا، بحيث يتم التعامل مع المكوّن ككيان منطقي واحد.

على سبيل المثال، قد يتكوّن عنصر Button من رمز فرعي وبعض النصوص. بدلاً من التعامل مع العناصر الفرعية كعناصر فردية، يدمج مكوّن Material Button دلالات العناصر الفرعية تلقائيًا، بحيث تتمكّن خدمات تسهيل الاستخدام من تجميعها وفقًا لذلك:

أزرار تتضمّن دلالات عناصر فرعية غير مدمجة مقابل دلالات عناصر فرعية مدمجة
الشكل 7. أزرار تتضمّن دلالات عناصر فرعية غير مدمجة مقابل دلالات عناصر فرعية مدمجة

وبالمثل، يؤدي استخدام المُعدِّل clickable أيضًا إلى دمج معاني العناصر التابعة في كيان واحد، يتم إرساله إلى خدمة مخصّصة لتسهيل الاستخدام مع تمثيل الإجراء المقابل:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

يمكنك أيضًا ضبط onClickLabel محدّد على العنصر الرئيسي القابل للنقر لتقديم معلومات إضافية لخدمات تسهيل الاستخدام وتقديم تمثيل أكثر دقة للإجراء:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

باستخدام TalkBack كمثال، فإنّ معدِّل clickable هذا وتصنيف النقر الخاص به سيتيحان لـ TalkBack تقديم تلميح إجراء "انقر مرّتين لفتح هذه المقالة"، بدلاً من الملاحظات التلقائية الأكثر عمومية "انقر مرّتين للتفعيل".

تتغيّر هذه الملاحظات حسب نوع الإجراء. ستؤدي النقرة الطويلة إلى تقديم تلميح TalkBack "انقر مرّتين مع الاستمرار على"، متبوعًا بتصنيف:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

في بعض الحالات، قد لا يكون لديك وصول مباشر إلى المُعدِّل clickable (على سبيل المثال، عندما يتم ضبطه في مكان ما في طبقة متداخلة منخفضة)، ولكنك لا تزال ترغب في تغيير تصنيف الإعلان من الإعداد الافتراضي. لإجراء ذلك، يمكنك تقسيم عملية ضبط clickable عن تعديل الإشعار باستخدام المُعدِّل semantics وضبط تصنيف النقر هناك لتعديل تمثيل الإجراء:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

لست بحاجة إلى تمرير إجراء النقر مرّتين. تتولى واجهات برمجة التطبيقات الحالية في Compose، مثل clickable أو Button، هذه المهمة نيابةً عنك. يتحقق منطق الدمج من اتخاذ إجراء وتسمية المُعدِّل الخارجي للمعلومات الموجودة. في المثال السابق، يمرّر NestedArticleListItem تلقائيًا إجراء النقر openArticle() إلى دلالات clickable. يمكنك ترك إجراء النقر فارغًا في إجراء تعديل الدلالات الثاني. ومع ذلك، يتم أخذ تصنيف النقر من المُعدِّل الدلالي الثاني onClick(label = "Open this document") لأنه لم يكن موجودًا في الأول.

قد تواجه سيناريوهات تتوقع فيها دمج دلالات العناصر الفرعية في عنصر رئيسي، ولكن هذا لا يحدث. يمكنك الاطّلاع على الدمج والمحو لمزيد من المعلومات التفصيلية.

المكوّنات المخصّصة

عند إنشاء مكوّن مخصّص، راجِع طريقة تنفيذ مكوّن مشابه في مكتبة Material أو مكتبات Compose الأخرى. بعد ذلك، قم بمحاكاة أو تعديل سلوك إمكانية الوصول حسب الاقتضاء. على سبيل المثال، إذا استبدلت Checkbox التصميم المتعدد الأبعاد بعملية التنفيذ الخاصة بك، فإن النظر إلى عملية تنفيذ Checkbox الحالية سيذكرك بإضافة المعدّل triStateToggleable، الذي يتعامل مع خصائص أدوات تسهيل الاستخدام للمكوّن. بالإضافة إلى ذلك، يمكنك استخدام معدِّلات Foundation بكثرة، لأنّها تتضمّن اعتبارات مدمجة لتسهيل الاستخدام وممارسات Compose الحالية التي يتناولها هذا القسم.

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