כדי לעזור לאנשים עם צורכי נגישות להשתמש באפליקציה, צריך לעצב שתומכת בדרישות הנגישות העיקריות.
כדאי להביא בחשבון את הגדלים המינימליים של משטחי המגע
כל אלמנט שמופיע במסך שמשתמשים יכולים ללחוץ עליו, לגעת בו או לבצע איתו אינטראקציה, צריך להיות שהוא מספיק גדול לאינטראקציה אמינה. כאשר משנים את הגודל של הרכיבים האלה, חשוב לוודא להגדיר את הגודל המינימלי ל-48dp כדי להתאים בצורה נכונה ל-Material Design הנחיות בנושא נגישות.
רכיבי חומר - כמו Checkbox
, RadioButton
, Switch
,
Slider
וגם Surface
– מגדירים את הגודל המינימלי הזה באופן פנימי, אבל רק
כשהרכיב יכול לקבל פעולות משתמש. לדוגמה, כשיש ב-Checkbox
הפרמטר onCheckedChange
מוגדר לערך שאינו null, תיבת הסימון כוללת
רוחב וגובה של 48dp לפחות.
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
כשהפרמטר onCheckedChange
מוגדר כ-null, המרווח הפנימי לא
כי לא ניתן לקיים אינטראקציה ישירה עם הרכיב.
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
כשמטמיעים בקרות בחירה כמו Switch
, RadioButton
או
Checkbox
, בדרך כלל מעלים את ההתנהגות הקליקבילית למאגר הורה, מגדירים
קריאה חוזרת (callback) של הקליק בתוכן הקומפוזבילי ל-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) } } }
כשהגודל של תוכן קומפוזבילי קליקבילי קטן מיעד המגע המינימלי גודל, 'כתיבה' עדיין מגדיל את משטח המגע. היא עושה זאת באמצעות הרחבת גודל משטח המגע נמצא מחוץ לגבולות של התוכן הקומפוזבילי.
בדוגמה הבאה יש 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 ) ) { // .. } }
לחלופין, אם אין לך גישה ללחצן הצירוף שניתן ללחוץ עליו, הגדר תווית הקליק במגביל הסמנטיקה:
@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 לא יכולה לתאר אותה באופן חזותי משתמש עם מוגבלות. ל-framework של Android נדרש תיאור טקסט נוסף נוסף של הסמל.
הפרמטר contentDescription
מתאר רכיב חזותי. שימוש בשפה
מחרוזת, כפי שהיא גלויה למשתמש.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
חלק מהאלמנטים הוויזואליים הם רק קישוטיים, וייתכן שלא תרצו לתקשר
אותם למשתמש. כשמגדירים את הפרמטר contentDescription
ל-null
,
לציין ל-framework של 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
אלמנטים של פריט ברשימה מתמזגים יחד, והנגישות
רואה אותם כרכיב אחד.
יכול להיות שיש קבוצה של תכנים קומפוזביליים שיוצרים קבוצה לוגית, לא ניתן ללחוץ על הקבוצה או שהיא חלק מפריט ברשימה. עדיין יהיה צורך בנגישות כדי להציג אותם כרכיב אחד. לדוגמה, דמיינו תוכן קומפוזבילי מציג את הדמות של המשתמש, את השם שלו ומידע נוסף:
אפשר להפעיל את התכונה 'כתיבה' כדי למזג את הרכיבים האלה באמצעות 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
לסמנטיקה,
ה-framework של Android משתמש כדי להקריא את המצב שבו נמצא התוכן הקומפוזבילי. עבור
לדוגמה, תוכן קומפוזבילי שניתן להחליף יכול להיות בשדה 'מסומן' או טקסט 'לא מסומן'
. במקרים מסוימים, יכול להיות שתרצו לשנות את תיאור ברירת המחדל של המצב
התוויות שמשמשות את 'פיתוח נייטיב'. אפשר לעשות זאת באמצעות ציון מפורש של המדינה
תוויות תיאור לפני שמגדירים תוכן קומפוזבילי כחומר שניתן להחליף מצב:
@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, מאפשרים למשתמשים לנווט ישירות מהכותרת העליונה.
בקטע 'פיתוח', מציינים שתוכן קומפוזבילי הוא כותרת על ידי הגדרת
נכס semantics
:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
איך מטפלים בתכנים קומפוזביליים בהתאמה אישית
בכל פעם שמחליפים רכיבי Material מסוימים באפליקציה בפריטים מותאמים אישית וצריך להביא בחשבון את שיקולי הנגישות.
נניח שאתם מחליפים את 'חומר' Checkbox
ביישום משלכם.
אפשר לשכוח להוסיף את מקש הצירוף triStateToggleable
, שמטפל
את מאפייני הנגישות של רכיב זה.
ככלל אצבע, מומלץ לבדוק את ההטמעה של הרכיב ספריית Material שמחקה כל התנהגות נגישות שניתן למצוא. בנוסף, כדאי לעשות שימוש רב בהתאמות של הבסיס, ולא ברמת ממשק המשתמש מגבילי התאמה, כי הם כוללים שיקולי נגישות שניתנים להתאמה אישית.
בדיקת ההטמעה של הרכיבים המותאמים אישית באמצעות כמה שירותי נגישות כדי לאמת את התנהגותו.
מקורות מידע נוספים
- נגישות: מושגים בסיסיים שיטות הנפוצות לכל פיתוח אפליקציות ל-Android
- יצירת אפליקציות נגישות: שלבים חשובים שאפשר לבצע כדי שהאפליקציה תהיה נגישה יותר
- עקרונות לשיפור האפליקציה נגישות: עקרונות מרכזיים חשוב לזכור שמשפרים את הנגישות של האפליקציה
- בדיקות נגישות: עקרונות בדיקה וכלים לנגישות ב-Android