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

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

ב-Android יש כמה שירותי נגישות של המערכת, כולל:

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

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

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

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

רכיבי התווית

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

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

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

ב-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.

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

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

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

ב-Jetpack Compose, משתמשים ברכיבים קומפוזביליים מובנים כמו 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

צפייה בתוכן