עקרונות לשיפור הנגישות של אפליקציות

כדי לעזור למשתמשים עם צרכי נגישות, מסגרת Android מאפשרת ליצור שירות נגישות שיכול להציג תוכן מאפליקציות למשתמשים וגם להפעיל אפליקציות בשמם.

‫Android מספקת כמה שירותי נגישות של המערכת, כולל:

  • TalkBack: עוזר לאנשים עם ליקויי ראייה או עיוורים. הוא מכריז על תוכן באמצעות קול מסונתז ומבצע פעולות באפליקציה בתגובה לתנועות של המשתמש.
  • גישה באמצעות מתג: עוזרת לאנשים עם מוגבלויות מוטוריות. היא מדגישה רכיבים אינטראקטיביים ומבצעת פעולות בתגובה ללחיצה של המשתמש על לחצן. ההגדרה הזו מאפשרת לשלוט במכשיר באמצעות לחצן אחד או שניים בלבד.

כדי לעזור לאנשים עם צרכי נגישות להשתמש באפליקציה שלכם בהצלחה, האפליקציה צריכה לעמוד בשיטות המומלצות שמתוארות בדף הזה, שמבוססות על ההנחיות שמתוארות במאמר שיפור הנגישות של אפליקציות.

כל אחת מהשיטות המומלצות האלה, שמתוארות בקטעים הבאים, יכולה לשפר עוד יותר את הנגישות של האפליקציה:

רכיבי תוויות
המשתמשים צריכים להבין את התוכן והמטרה של כל רכיב אינטראקטיבי ומשמעותי בממשק המשתמש באפליקציה.
הוספת פעולות נגישות
הוספת פעולות נגישות מאפשרת למשתמשים בשירותי נגישות להשלים תהליכי משתמש קריטיים באפליקציה שלכם.
שימוש בתכונות נגישות מובנות
כברירת מחדל, Compose מציעה הרבה התנהגויות שקשורות לנגישות. כדי להפוך את הרכיבים לנגישים בלי להשקיע מאמץ נוסף, או עם השקעה מינימלית, אפשר להשתמש בהתנהגויות נגישות מוגדרות מראש. ב-Compose יש גם דרכים לתמוך בדרישות נגישות ספציפיות יותר שלא נכללות בתכונות ברירת המחדל.
שימוש ברמזים שאינם צבע
המשתמשים צריכים להיות מסוגלים להבחין בבירור בין קטגוריות של אלמנטים בממשק משתמש. כדי לעשות את זה, צריך להשתמש בהדפסים ובמיקום, בנוסף לצבע, כדי להבליט את ההבדלים האלה.
שיפור הנגישות של תוכן מדיה
מוסיפים תיאורים לתוכן הווידאו או האודיו באפליקציה, כדי שמשתמשים שצורכים את התוכן הזה לא יצטרכו להסתמך רק על רמזים חזותיים או קוליים.

רכיבי התווית

חשוב לספק למשתמשים תוויות שימושיות ותיאוריות לכל רכיב בממשק המשתמש אינטראקטיבי באפליקציה. כל תווית צריכה להסביר את הסמנטיקה של רכיב מסוים – כלומר, את המשמעות והמטרה של הרכיב. קוראי מסך כמו TalkBack יכולים להקריא את התוויות האלה למשתמשים.

ברוב המקרים, ממשקי ה-API של Compose ו-Material כוללים תמיכה נגישות כברירת מחדל. עם זאת, אם אתם צריכים לציין באופן ידני את המאפיינים הסמנטיים של רכיב בממשק המשתמש, אתם יכולים להשתמש במאפיין contentDescription ובמשנה semantics. מידע נוסף על סמנטיקה זמין במאמר סמנטיקה.

בקטעים הבאים מתוארות כמה טכניקות נוספות של תיוג.

רכיבים שאפשר לערוך

כשמוסיפים תוויות לרכיבים שניתנים לעריכה, כמו שדות טקסט, כדאי להציג טקסט שנותן דוגמה לקלט תקין ברכיב עצמו, וגם להפוך את הטקסט הזה לזמין לקוראי מסך. במקרים כאלה, אפשר להשתמש בטקסט פלייסהולדר, שנקרא גם טקסט רמז.

בדוגמה הבאה, לרכיב 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.

כך שירותי הנגישות יכולים להציג את תיאורי התוכן של הרכיבים הפנימיים אחד אחרי השני, בהודעה אחת. איחוד של רכיבים קשורים עוזר למשתמשים בטכנולוגיות מסייעות לגלות את המידע במסך בצורה יעילה יותר.

בקטע הקוד הבא, רכיב ה-Composable‏ 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 (רמת API‏ 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?")
    }
}

מידע נוסף על ניקוי סמנטיקה זמין במאמר ניקוי והגדרת סמנטיקה.

הוספת פעולות נגישות

חשוב לוודא שלמשתמשים בשירותי נגישות יש דרך להשלים את כל תהליכי המשתמש באפליקציה.

אם האינטראקציה של רכיב ה-Composable המותאם אישית משנה את מצב האפליקציה באופן לא ברור, צריך לספק תוויות תיאוריות לפעולות הקשה רגילות באמצעות פרמטרים כמו onClickLabel או onLongClickLabel ב-Modifier.clickable או ב-Modifier.combinedClickable.

לפעולות מורכבות שלא ניתן למפות להקשות רגילות, משתמשים ב-customActions.

לדוגמה, אם האפליקציה מאפשרת למשתמשים לגרור פריט למיקום אחר או להחליק על פריט ברשימה, אפשר לספק דרך חלופית להשלמת תהליכי המשתמש האלה על ידי חשיפת הפעולה לשירותי נגישות. כך משתמשים ב-TalkBack, ב-Voice Access או ב-גישה באמצעות מתג יכולים לבצע פעולות שאולי זמינות רק באמצעות מחוות.

ב-Compose, אפשר להגדיר פעולות נגישות מותאמות אישית באמצעות המאפיין customActions במאפיין modifier‏ 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.

מידע נוסף על תגובה להקשות וללחיצות זמין במאמרים הקשה ולחיצה ורכיבים אינטראקטיביים.

שימוש בתכונות נגישות מובנות

כשמעצבים את ממשק המשתמש של האפליקציה, כדאי להשתמש בתכונות נגישות מובנות כדי להימנע מהטמעה מחדש של פונקציונליות שכבר קיימת. ממשקי ה-API של Material,‏ Compose UI ו-Foundation מיישמים ומציעים כברירת מחדל הרבה שיטות נגישות.

ב-Jetpack פיתוח נייטיב, אפשר להשתמש ברכיבים קומפוזביליים מובנים כמו Button,‏ Switch ו-Checkbox כדי ליצור ממשקי משתמש נגישים. הרכיבים האלה מגיעים עם semantics מגבילים, כמו role ו-stateDescription, שאפשר להשתמש בהם כדי להפוך את האפליקציות לנגישות יותר.

החלת סמנטיקה על רכיבים מותאמים אישית

כשיוצרים רכיב בהתאמה אישית, חשוב לחשוב על סוג התמיכה בנגישות שהרכיב הזה צריך כדי למלא את התפקיד שלו. בדרך כלל, ממשקי ה-API הרגילים של 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. דוגמאות ליצירת רכיבי ממשק משתמש באמצעות צבע בלבד (מימין) ובאמצעות צבע, צורות וטקסט (משמאל).

שיפור הנגישות של תוכן מדיה

אם אתם מפתחים אפליקציה שכוללת תוכן מדיה, כמו קליפ וידאו או הקלטת אודיו, כדאי לנסות לתמוך במשתמשים עם סוגים שונים של צרכי נגישות כדי להבין את החומר. בפרט, כדאי לנסות את הפעולות הבאות:

  • כוללים אמצעי בקרה שמאפשרים למשתמשים להשהות או לעצור את המדיה, לשנות את עוצמת הקול ולהפעיל או להשבית כתוביות.
  • אם בסרטון מוצג מידע שחיוני להשלמת תהליך עבודה, צריך לספק את אותו התוכן בפורמט חלופי, כמו תמליל.

מקורות מידע נוספים

למידע נוסף על שיפור הנגישות של האפליקציה, אפשר לעיין במקורות המידע הנוספים הבאים:

Codelabs

צפייה בתוכן