مبادئ تحسين إمكانية الوصول إلى التطبيق

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

يوفّر Android العديد من خدمات تسهيل الاستخدام في النظام، بما في ذلك ما يلي:

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

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

يمكن لكل من أفضل الممارسات التالية، الموضحة في الأقسام التالية، تحسين إمكانية الوصول إلى تطبيقك:

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

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

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

في معظم الحالات، تتوفّر ميزات تسهيل الاستخدام التلقائية في واجهات برمجة التطبيقات في Compose وMaterial. ومع ذلك، إذا كنت بحاجة إلى تحديد الخصائص الدلالية لعنصر في واجهة المستخدم يدويًا، فاستخدم المعدّل semantics والخاصية contentDescription. لمزيد من المعلومات حول الدلالات، يُرجى الاطّلاع على الدلالات.

توضّح الأقسام التالية العديد من أساليب التصنيف الأخرى.

العناصر القابلة للتعديل

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

في المثال التالي، يحتوي TextField على مَعلمة placeholder توفّر نص تلميح.

val usernameState = rememberTextFieldState()
TextField(
    state = usernameState,
    lineLimits = TextFieldLineLimits.SingleLine,
    placeholder = { Text("Enter Username") }
)

من الشائع أيضًا أن يكون لحقل النص تصنيف وصفي موافِق يوضّح ما يجب على المستخدمين إدخاله.

في المثال التالي، يحتوي TextField على مَعلمة label تقدّم وصفًا مناسبًا.

TextField(
    state = rememberTextFieldState(initialText = "Hello"),
    label = { Text("Label") }
)

لمزيد من المعلومات حول النص وإدخال المستخدم، يُرجى الاطّلاع على ضبط حقول النص.

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

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

على سبيل المثال، عندما يكون لديك LazyColumn أو LazyRow، استخدِم المُعدِّل semantics لتعيين collectionItemInfo فريد لكل عنصر، كما هو موضّح في المقتطف التالي:

MilkyWayList(
    modifier = Modifier
        .semantics {
            collectionInfo = CollectionInfo(
                rowCount = milkyWay.count(),
                columnCount = 1
            )
        }
) {
    milkyWay.forEachIndexed { index, text ->
        Text(
            text = text,
            modifier = Modifier.semantics {
                collectionItemInfo =
                    CollectionItemInfo(index, 0, 0, 0)
            }
        )
    }
}

لمزيد من المعلومات حول سمات الدلالات للقوائم والشبكات، يُرجى الاطّلاع على معلومات القائمة والعناصر.

مجموعات المحتوى ذي الصلة

إذا كان تطبيقك يعرض عدة عناصر في واجهة المستخدم تشكّل مجموعة طبيعية، مثل تفاصيل أغنية أو سمات رسالة، يمكنك ترتيب هذه العناصر داخل حاوية رئيسية (مثل Column أو Row أو Box). استخدِم المُعدِّل semantics للحاوية الرئيسية لضبط mergeDescendants على true.

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

في المقتطف التالي، تعمل الدالة المركّبة Row كحاوية رئيسية. داخل Row توجد عناصر ذات صلة تعرض البيانات الوصفية لمنشور مدونة - الصورة الرمزية للمؤلف واسم المؤلف ووقت القراءة المقدر. يؤدي ضبط السمة mergeDescendants على القيمة true إلى تجميع هذه العناصر الداخلية، وبالتالي يمكن لخدمات تسهيل الاستخدام التعامل معها كوحدة واحدة.

@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")
        }
    }
}

عند تجميع العناصر ذات الصلة كما في المثال السابق، اجعل الحاوية الرئيسية فقط تفاعلية. تجنَّب إضافة المُعدِّلات clickable أو focusable إلى العناصر الفرعية الداخلية. بدلاً من ذلك، طبِّق المعدِّلات على العنصر الرئيسي Row أو Column.

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

ملاحظة: بشكل عام، عند إنشاء وصف محتوى لمجموعة، تجنَّب تجميع نص العناصر الثانوية. يؤدي ذلك إلى جعل وصف المجموعة هشًا، وعندما يتغير نص العنصر الفرعي، قد لا يتطابق وصف المجموعة مع النص المرئي.

في سياق قائمة أو شبكة، قد يدمج قارئ الشاشة نص العُقد النصية الفرعية لعنصر القائمة أو الشبكة. من الأفضل تجنُّب تعديل هذا الإشعار.

لمزيد من المعلومات حول دمج الدلالات، يُرجى الاطّلاع على الدمج والمسح.

العناوين داخل النص

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

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

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

لمزيد من المعلومات حول السمة الدلالية heading، يُرجى الاطّلاع على العناوين.

عناوين لوحات تسهيل الاستخدام

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

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

ShareSheet(
    message = "Choose how to share this photo",
    modifier = Modifier
        .fillMaxWidth()
        .align(Alignment.TopCenter)
        .semantics { paneTitle = "New bottom sheet" }
)

لمزيد من المعلومات حول سمة دلالات paneTitle، يُرجى الاطّلاع على المكوّنات الشبيهة بالنوافذ.

العناصر الزخرفية

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

بالنسبة إلى الدوال المركّبة Image أو Icon، اضبط السمة contentDescription = null. بالنسبة إلى العناصر الأخرى التي تقتصر على الزخرفة ولا توفّر أي سياق أو وظيفة، يمكنك استخدام hideFromAccessibility. تطلب هذه السمة الدلالية من خدمات تسهيل الاستخدام تجاهُل العنصر.

إذا كانت الدالة المركّبة التفاعلية تحتوي على عناصر فرعية تزيينية غير تفاعلية، استخدِم clearAndSetSemantics للتأكّد من أنّ خدمات تسهيل الاستخدام لا تتجاوزها. يُرجى العِلم أنّ السمة clearAndSetSemantics تمحو تمامًا دلالات العنصر وعناصره الفرعية التلقائية. يتيح لك ذلك تحديد عنصر جديد وموحّد لتسهيل الاستخدام. عادةً ما تستخدم هذا الأسلوب للمكونات المخصصة المعقدة.

في المثال التالي، Icon وText هما عنصران فرعيان مزخرفان داخل زر تبديل مخصّص. لمنع خدمات تسهيل الاستخدام من التنقّل بين هذه العناصر الفرعية بشكل فردي، يمكنك محو دلالاتها باستخدام clearAndSetSemantics في العنصر الرئيسي Row. يؤدي ذلك إلى إعلام خدمات تسهيل الاستخدام بأنّ عليها التعامل مع Row بالكامل كمفتاح تبديل يمكن التنقّل فيه:

// Developer might intend this to be a toggleable.
// Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied,
// a custom description is set, and a Role is applied.

@Composable
fun FavoriteToggle() {
    val checked = remember { mutableStateOf(true) }
    Row(
        modifier = Modifier
            .toggleable(
                value = checked.value,
                onValueChange = { checked.value = it }
            )
            .clearAndSetSemantics {
                stateDescription = if (checked.value) "Favorited" else "Not favorited"
                toggleableState = ToggleableState(checked.value)
                role = Role.Switch
            },
    ) {
        Icon(
            imageVector = Icons.Default.Favorite,
            contentDescription = null // not needed here

        )
        Text("Favorite?")
    }
}

لمزيد من المعلومات حول محو الدلالات، يُرجى الاطّلاع على محو الدلالات وضبطها.

إضافة إجراءات تسهيل الاستخدام

من المهم التأكّد من أنّ مستخدمي خدمات تسهيل الاستخدام لديهم طريقة لإكمال جميع مسارات المستخدمين في تطبيقك.

إذا كان تفاعل الدالة المركّبة المخصّصة يغيّر حالة التطبيق بطريقة غير واضحة، قدِّم تصنيفات وصفية لإجراءات النقر العادية باستخدام مَعلمات مثل onClickLabel أو onLongClickLabel في Modifier.clickable أو Modifier.combinedClickable.

بالنسبة إلى التفاعلات المعقّدة التي لا يمكن ربطها بالنقرات العادية، استخدِم customActions.

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

في Compose، يمكنك تحديد إجراءات مخصّصة لتسهيل الاستخدام من خلال السمة customActions في المُعدِّل semantics، باستخدام CustomAccessibilityAction.

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

SwipeToDismissBox(
    modifier = Modifier.semantics {
        // Represents the swipe to dismiss for accessibility
        customActions = listOf(
            CustomAccessibilityAction(
                label = "Remove article from list",
                action = {
                    removeArticle()
                    true
                }
            )
        )
    },
    state = rememberSwipeToDismissBoxState(),
    backgroundContent = {}
) {
    ArticleListItem()
}

بعد تنفيذ إجراء تسهيل الاستخدام المخصّص، يمكن للمستخدمين الوصول إلى الإجراء من خلال قائمة الإجراءات.

لمزيد من المعلومات عن الإجراءات المخصّصة، يُرجى الاطّلاع على الإجراءات المخصّصة.

توضيح الإجراءات المتاحة

عندما يتيح عنصر في واجهة المستخدم إجراءات مثل النقر مع الاستمرار، تعلن خدمة تسهيل الاستخدام، مثل TalkBack، عن ذلك على النحو التالي: "انقر مرّتين مع الاستمرار للضغط مع الاستمرار".

لا يقدّم هذا الإشعار العام للمستخدم أي سياق حول ما يفعله إجراء اللمس مع الاستمرار.

لجعل هذا الإعلان أكثر فائدة، حدد وصفًا مفيدًا للإجراء.

في Compose، تتضمّن معدِّلات التفاعل العادية، مثل clickable وcombinedClickable، مَعلمات مدمجة (أي onClickLabel وonLongClickLabel) يمكنك استخدامها لتقديم أوصاف للإجراءات، كما في المثال التالي:

var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) }
val haptics = LocalHapticFeedback.current
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) {
    items(photos, { it.id }) { photo ->
        ImageItem(
            photo,
            Modifier
                .combinedClickable(
                    onClick = { activePhotoId = photo.id },
                    onLongClick = {
                        haptics.performHapticFeedback(HapticFeedbackType.LongPress)
                        contextMenuPhotoId = photo.id
                    },
                    onLongClickLabel = stringResource(R.string.open_context_menu)
                )
        )
    }
}
if (contextMenuPhotoId != null) {
    PhotoActionsSheet(
        photo = photos.first { it.id == contextMenuPhotoId },
        onDismissSheet = { contextMenuPhotoId = null }
    )
}

يؤدي ذلك إلى إعلان TalkBack عن "فتح قائمة السياق"، ما يساعد المستخدمين في فهم الغرض من الإجراء.

يمكنك أيضًا تحديد تصنيف مباشرةً في المُعدِّل semantics.

لمزيد من المعلومات حول الاستجابة للنقرات، يُرجى الاطّلاع على النقر والضغط والعناصر التفاعلية.

استخدام ميزات تسهيل الاستخدام المضمَّنة

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

في Jetpack Compose، استخدِم الدوال المركّبة المضمّنة، مثل Button وSwitch وCheckbox لإنشاء واجهات مستخدم تسهّل الاستخدام. تتضمّن هذه المكوّنات مُعدِّلات semantics، مثل role وstateDescription، والتي يمكنك استخدامها لتسهيل استخدام تطبيقاتك.

تطبيق الدلالات على المكوّنات المخصّصة

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

ومع ذلك، تتطلب بعض المكونات معلومات أكثر تحديدًا مما توفره المعدلات القياسية. في هذه الحالات، ابحث عن معدِّلات متخصّصة (مثل triStateToggleable) أو، في حال عدم توفّر أي منها، يمكنك تقديم الدلالات بشكل صريح باستخدام Modifier.semantics المنخفض المستوى.

على سبيل المثال، لنفترض أنّ لديك TriStateSwitch، وهو مفتاح تبديل يتضمّن ثلاث حالات (مفعَّل وغير مفعَّل وغير محدّد).

في حين أنّ معدِّل toggleable العادي يفترض حالتين، يتعامل معدِّل triStateToggleable مع تعقيد الحالة الثالثة. ويتم ضبط إعدادات تسهيل الاستخدام Role (Switch) وState تلقائيًا. بهذه الطريقة، تتلقّى الخدمات المخصصة لتسهيل الاستخدام معلومات دقيقة، ولن تحتاج إلى تحديد الدلالات يدويًا.

يعرض مقتطف الرمز التالي TriStateSwitch باستخدام هذا الأسلوب:

@Composable
fun TriStateSwitch(
    state: ToggleableState,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    // A real implementation would include custom drawing for the switch.
    // This example uses a Box to demonstrate the semantics.
    Box(
        modifier = modifier
            .size(width = 64.dp, height = 40.dp)
            // triStateToggleable handles the semantics (Role and State)
            // automatically, so explicit Modifier.semantics is not needed here.
            .triStateToggleable(
                state = state,
                onClick = onClick,
                role = Role.Switch
            )
            // Add visual feedback based on the state
            .background(
                when (state) {
                    ToggleableState.On -> Color.Green
                    ToggleableState.Off -> Color.Gray
                    ToggleableState.Indeterminate -> Color.Yellow
                }
            )
    )
}

// Usage within another composable:
var state by remember { mutableStateOf(ToggleableState.Off) }
TriStateSwitch(
    state = state,
    onClick = {
        state = when (state) {
            ToggleableState.Off -> ToggleableState.Indeterminate
            ToggleableState.Indeterminate -> ToggleableState.On
            ToggleableState.On -> ToggleableState.Off
        }
    }
)

عند إنشاء مكوّن مخصّص، احرص على توفير جميع السمات الدلالية ذات الصلة لأغراض تسهيل الاستخدام. على سبيل المثال، إذا كان المكوّن يحاكي عنصر تحكّم عاديًا مثل مفتاح تبديل أو زر، تشمل هذه السمات دور المكوّن (مثل Role.Switch أو Role.Button) وstateDescription (مثل "مفعّل" أو "غير مفعّل" أو "محدّد" أو "غير محدّد") وأي تصنيفات إجراءات ذات صلة. لمزيد من المعلومات، يُرجى الاطّلاع على المكوّنات المخصّصة.

استخدام إشارات غير اللون

لمساعدة المستخدمين الذين يعانون من قصور في رؤية الألوان، استخدِم إشارات أخرى غير الألوان لتمييز عنصر في واجهة المستخدم ضمن شاشات تطبيقك. ويمكن أن تشمل هذه الأساليب استخدام أشكال أو أحجام مختلفة، أو توفير أنماط نصية أو مرئية، أو إضافة ملاحظات صوتية أو لمسية (تجاوب حسّي) لتحديد الاختلافات بين العناصر.

يعرض الشكل 1 نسختين من نشاط ما. يستخدم أحد الإصدارين اللون فقط للتمييز بين إجراءين محتملين في سير العمل. يستخدم الإصدار الآخر أفضل ممارسة لتضمين الأشكال والنصوص بالإضافة إلى اللون لإبراز الاختلافات بين الخيارين:

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

تسهيل الوصول إلى محتوى الوسائط

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

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

مراجع إضافية

لمزيد من المعلومات حول جعل تطبيقك أكثر سهولة في الاستخدام، راجع الموارد الإضافية التالية:

اختبارات الرموز

محتوى المشاهدات