الخطوات الرئيسية لتحسين إمكانية الوصول في ComposeAllowed

لمساعدة الأشخاص ذوي الاحتياجات الخاصة بإمكانية الوصول على استخدام تطبيقك بنجاح، صمم دعم المتطلبات الرئيسية لإمكانية الوصول.

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

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

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

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

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

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

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

عند تنفيذ عناصر التحكّم في الاختيار، مثل 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)
        }
    }
}

عندما يكون حجم عنصر قابل للنقر أصغر من الحدّ الأدنى لمساحات اللمس الحجم، تستمر ميزة 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)
        )
    }
}

لمنع التداخل المحتمل بين مناطق اللمس لعناصر مختلفة قابلة للإنشاء، استخدم حدًا أدنى كبيرًا بدرجة كافية للعنصر القابل للإنشاء. في المثال، قد يكون ذلك يعني استخدام مفتاح التعديل 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)
        )
    }
}

إضافة تصنيفات النقرات

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

اضبط تصنيف النقر من خلال ضبط مَعلمة في أداة التعديل clickable:

@Composable
private fun ArticleListItem(openArticle: () -> Unit) {
    Row(
        Modifier.clickable(
            // R.string.action_read_article = "read article"
            onClickLabel = stringResource(R.string.action_read_article),
            onClick = openArticle
        )
    ) {
        // ..
    }
}

إذا لم يكن بإمكانك الوصول إلى مفتاح التعديل القابل للنقر، اضبط تصنيف النقر في مفتاح التعديل semantics:

@Composable
private fun LowLevelClickLabel(openArticle: () -> Boolean) {
    // R.string.action_read_article = "read article"
    val readArticleLabel = stringResource(R.string.action_read_article)
    Canvas(
        Modifier.semantics {
            onClick(label = readArticleLabel, action = openArticle)
        }
    ) {
        // ..
    }
}

وصف العناصر المرئية

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

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

شريط من الرموز القابلة للنقر، مع

واستنادًا إلى الرمز وحده، لا يمكن لإطار عمل 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 اسأل نفسك عما إذا كان العنصر ينقل المعلومات التي سيحتاج المستخدم إلى أداء مهمته. وإذا لم يكن كذلك، فمن الأفضل ترك ووصفها.

دمج العناصر

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

عند تطبيق مفتاح التعديل clickable على عنصر قابل للإنشاء، انقر على "إنشاء" تدمج تلقائيًا جميع العناصر التي يحتوي عليها العنصر القابل للإنشاء. ينطبق هذا أيضًا على ListItem ودمج العناصر الموجودة داخل عنصر قائمة معًا، وإمكانية الوصول بالخدمات لعرضها كعنصر واحد.

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

مجموعة من عناصر واجهة المستخدم، بما في ذلك اسم المستخدم تم اختيار الاسم.

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

@Composable
private fun PostMetadata(metadata: Metadata) {
    // Merge elements below for accessibility purposes
    Row(modifier = Modifier.semantics(mergeDescendants = true) {}) {
        Image(
            imageVector = Icons.Filled.AccountCircle,
            contentDescription = null // decorative
        )
        Column {
            Text(metadata.author.name)
            Text("${metadata.date} • ${metadata.readTimeMinutes} min read")
        }
    }
}

تركز خدمات تسهيل الاستخدام الآن على الحاوية بالكامل في آنٍ واحد، ويتم دمجها محتوياتها:

مجموعة من عناصر واجهة المستخدم، بما في ذلك اسم المستخدم يتم تحديد جميع العناصر معًا.

إضافة إجراءات مخصّصة

ألقِ نظرة على عنصر القائمة التالي:

تمثّل هذه السمة عنصر قائمة نموذجيًا يحتوي على عنوان المقالة والمؤلف ورمز الإشارة.

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

عنصر القائمة، مع تحديد جميع العناصر معًا.

عنصر القائمة، مع تحديد رمز الإشارة فقط

في القائمة الطويلة، يمكن أن يصبح هذا التكرار متكررًا للغاية. هناك نهج أفضل وهو تحديد إجراء مخصص يسمح للمستخدم بوضع إشارة مرجعية على العنصر. ملاحظات يجب أخذها في الاعتبار أنه يجب عليك أيضًا إزالة سلوك رمز الإشارة نفسها للتأكد من عدم اختيارها بواسطة خدمة إمكانية الوصول. هذا النمط يتم باستخدام مفتاح التعديل clearAndSetSemantics:

@Composable
private fun PostCardSimple(
    /* ... */
    isFavorite: Boolean,
    onToggleFavorite: () -> Boolean
) {
    val actionLabel = stringResource(
        if (isFavorite) R.string.unfavorite else R.string.favorite
    )
    Row(
        modifier = Modifier
            .clickable(onClick = { /* ... */ })
            .semantics {
                // Set any explicit semantic properties
                customActions = listOf(
                    CustomAccessibilityAction(actionLabel, onToggleFavorite)
                )
            }
    ) {
        /* ... */
        BookmarkButton(
            isBookmarked = isFavorite,
            onClick = onToggleFavorite,
            // Clear any semantics properties set on this node
            modifier = Modifier.clearAndSetSemantics { }
        )
    }
}

وصف حالة العنصر

يمكن للمواد القابلة للإنشاء تحديد stateDescription للدلالات التي يستخدم إطار عمل Android لقراءة حالة العنصر القابل للإنشاء. بالنسبة على سبيل المثال، يمكن أن يكون العنصر القابل للتبديل في علامة أو "غير محددة" الولاية. في بعض الحالات، قد تحتاج إلى إلغاء وصف الحالة التلقائي. التصنيفات التي تستخدمها Compose. ويمكنك إجراء ذلك من خلال تحديد الولاية تصنيفات الأوصاف قبل تحديد عنصر قابل للإنشاء كعنصر قابل للتبديل:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}

تحديد العناوين

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

لقطة شاشة لمشاركة مدوّنة تتضمّن نص المقالة في حاوية قابلة للتمرير

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

في Compose، يمكنك الإشارة إلى أن العنصر القابل للإنشاء هو عنوان من خلال تحديد الخاصية "semantics":

@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}

التعامل مع عناصر مخصّصة قابلة للإنشاء

كلما تستبدل مكونات معينة Material في تطبيقك بمكونات مخصصة الإصدارات، يجب وضع اعتبارات إمكانية الوصول في الاعتبار.

لنفترض أنك ستستبدل سمة المواد Checkbox بعملية التنفيذ الخاصة بك. يمكنك نسيان إضافة مفتاح التعديل triStateToggleable الذي يتعامل مع خصائص إمكانية الوصول لهذا المكون.

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

يمكنك اختبار تنفيذ المكوِّن المخصّص باستخدام عدة إمكانية الوصول للتحقق من سلوكها.

مصادر إضافية