אם האפליקציה שלכם דורשת רכיב תצוגה מותאם אישית, אתם צריכים להפוך את התצוגה לנגישה יותר. השלבים הבאים יכולים לשפר את הנגישות של התצוגה המותאמת אישית, כמו שמתואר בדף הזה:
- טיפול בקליקים של בקר כיווני.
- הטמעה של שיטות API להנגשה.
- שליחת אובייקטים ספציפיים לתצוגה המותאמת אישית
AccessibilityEventשלכם. - מאכלסים את
AccessibilityEventואתAccessibilityNodeInfoבתצוגה המפורטת.
טיפול בקליקים של בקר כיווני
ברוב המכשירים, לחיצה על תצוגה באמצעות בקר כיווני שולחת KeyEvent עם KEYCODE_DPAD_CENTER לתצוגה שנמצאת כרגע במוקד ההתעניינות. כל התצוגות הרגילות ב-Android מטפלות ב-KEYCODE_DPAD_CENTER בצורה נכונה. כשיוצרים פקד Viewבהתאמה אישית, צריך לוודא שהאירוע הזה משפיע כמו הקשה על התצוגה במסך המגע.
הפקד המותאם אישית צריך להתייחס לאירוע KEYCODE_ENTER
KEYCODE_DPAD_CENTERבאותו אופן כמו אל KEYCODE_DPAD_CENTER. כך קל יותר למשתמשים לבצע אינטראקציות באמצעות מקלדת מלאה.
הטמעה של שיטות API להנגשה
אירועי נגישות הם הודעות לגבי אינטראקציות של משתמשים עם רכיבי הממשק החזותי של האפליקציה. ההודעות האלה מטופלות על ידי שירותי נגישות, שמשתמשים במידע שבאירועים האלה כדי ליצור משוב והנחיות נוספים. ה-methods של הנגישות הם חלק מהמחלקה View ומהמחלקה View.AccessibilityDelegate. אלה השיטות:
dispatchPopulateAccessibilityEvent()onPopulateAccessibilityEvent() לתצוגה הזו ואז לשיטה dispatchPopulateAccessibilityEvent() לכל צאצא של התצוגה הזו. onInitializeAccessibilityEvent()TextView או ל-Button פשוטים, צריך לבטל את השיטה הזו ולהגדיר את המידע הנוסף על התצוגה – כמו סוג שדה הסיסמה, סוג תיבת הסימון או מצבים שמספקים אינטראקציה או משוב למשתמש באירוע – באמצעות השיטה הזו. אם מבטלים את השיטה הזו, קוראים להטמעה שלה במחלקת העל ומשנים רק מאפיינים שלא מוגדרים על ידי מחלקת העל.onInitializeAccessibilityNodeInfo()View כברירת מחדל כוללת קבוצה רגילה של מאפייני תצוגה, אבל אם התצוגה המותאמת אישית מספקת אמצעי בקרה אינטראקטיביים מעבר ל-TextView או ל-Button פשוטים, צריך לבטל את השיטה הזו ולהגדיר את המידע הנוסף על התצוגה באובייקט AccessibilityNodeInfo שמטופל על ידי השיטה הזו.onPopulateAccessibilityEvent()AccessibilityEvent לתצוגה שלכם. היא נקראת גם אם התצוגה היא צאצא של תצוגה שמייצרת אירוע נגישות.
onRequestSendAccessibilityEvent()AccessibilityEvent. בשלב הזה ההורה יכול להוסיף מידע לאירוע הנגישות. מטמיעים את השיטה הזו רק אם לתצוגה המותאמת אישית יכולות להיות תצוגות צאצא, ואם תצוגת ההורה יכולה לספק מידע על ההקשר לאירוע הנגישות, מידע שימושי לשירותי הנגישות.sendAccessibilityEvent()- המערכת קוראת לשיטה הזו כשמשתמש מבצע פעולה בתצוגה. האירוע מסווג לפי סוג פעולת משתמש, כמו
TYPE_VIEW_CLICKED. באופן כללי, צריך לשלוחAccessibilityEventבכל פעם שהתוכן של התצוגה המותאמת משתנה. sendAccessibilityEventUnchecked()- השיטה הזו משמשת כשהקוד שקורא ל-method צריך לשלוט ישירות בבדיקה כדי לוודא שהנגישות מופעלת במכשיר (
AccessibilityManager.isEnabled()). אם מטמיעים את השיטה הזו, הקריאה מתבצעת כאילו הנגישות מופעלת, ללא קשר להגדרת המערכת. בדרך כלל לא צריך להטמיע את השיטה הזו בתצוגה בהתאמה אישית. dispatchPopulateAccessibilityEvent()onInitializeAccessibilityEvent()onInitializeAccessibilityNodeInfo()onPopulateAccessibilityEvent()TYPE_VIEW_CLICKEDTYPE_VIEW_FOCUSEDTYPE_VIEW_HOVER_ENTERTYPE_VIEW_HOVER_EXITTYPE_VIEW_LONG_CLICKEDTYPE_VIEW_SCROLLED- יצירת
AccessibilityEventמתאים לפעולת הלחיצה שפוענחה. - הפעלת שירותי נגישות כדי לבצע את פעולת הלחיצה המותאמת אישית עבור משתמשים שלא יכולים להשתמש במסך מגע.
כדי לתמוך בנגישות, צריך לבטל את השיטות הקודמות לנגישות ולהטמיע אותן ישירות במחלקה של התצוגה המותאמת אישית.
לפחות, צריך להטמיע את שיטות הנגישות הבאות עבור מחלקת התצוגה המותאמת אישית:
מידע נוסף על הטמעת השיטות האלה זמין בקטע בנושא איכלוס אירועי נגישות.
שליחת אירועי נגישות
בהתאם לפרטים של התצוגה המותאמת אישית, יכול להיות שיהיה צורך לשלוח AccessibilityEvent אובייקטים בזמנים שונים או לאירועים שלא מטופלים על ידי הטמעה שמוגדרת כברירת מחדל. הסיווג View מספק הטמעה של ברירת מחדל לסוגי האירועים הבאים:
באופן כללי, צריך לשלוח AccessibilityEvent בכל פעם שהתוכן של התצוגה המותאמת משתנה. לדוגמה, אם מטמיעים פס הזזה מותאם אישית שמאפשר למשתמש לבחור ערך מספרי על ידי לחיצה על מקש החץ שמאלה או חץ ימינה, התצוגה המותאמת אישית צריכה לפלוט אירוע של TYPE_VIEW_TEXT_CHANGED בכל פעם שערך פס ההזזה משתנה. דוגמת הקוד הבאה ממחישה את השימוש בשיטה sendAccessibilityEvent() כדי לדווח על האירוע הזה.
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when(keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> { currentValue-- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) true } ... } }
Java
@Override public boolean onKeyUp (int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { currentValue--; sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); return true; } ... }
איכלוס של אירועי נגישות
לכל AccessibilityEvent יש קבוצה של מאפיינים נדרשים שמתארים את המצב הנוכחי של התצוגה. המאפיינים האלה כוללים פרטים כמו שם המחלקה של התצוגה, תיאור התוכן ומצב הסימון. המאפיינים הספציפיים שנדרשים לכל סוג אירוע מתוארים במסמכי העזר AccessibilityEvent.
ההטמעה של View מספקת ערכי ברירת מחדל למאפייני החובה האלה. ערכים רבים, כולל שם המחלקה וחותמת הזמן של האירוע, מסופקים באופן אוטומטי. אם אתם יוצרים רכיב תצוגה מותאם אישית, אתם צריכים לספק מידע על התוכן והמאפיינים של התצוגה. המידע הזה יכול להיות פשוט כמו תווית של לחצן, והוא יכול לכלול מידע נוסף על המצב שרוצים להוסיף לאירוע.
אפשר להשתמש בשיטות onPopulateAccessibilityEvent() ו-onInitializeAccessibilityEvent() כדי לאכלס או לשנות את המידע ב-AccessibilityEvent. משתמשים בשיטה onPopulateAccessibilityEvent() במיוחד כדי להוסיף או לשנות את תוכן הטקסט של האירוע, שהופך להנחיות קוליות על ידי שירותי נגישות כמו TalkBack. משתמשים בשיטה onInitializeAccessibilityEvent() כדי לאכלס מידע נוסף על האירוע, כמו מצב הבחירה של התצוגה.
בנוסף, מטמיעים את ה-method
onInitializeAccessibilityNodeInfo(). שירותי נגישות משתמשים באובייקטים AccessibilityNodeInfo שאוכלסו על ידי השיטה הזו כדי לבדוק את היררכיית התצוגה שמייצרת אירוע נגישות אחרי שהוא מתקבל, ולספק משוב מתאים למשתמשים.
בדוגמת הקוד הבאה אפשר לראות איך לבטל את שלוש המתודות האלה בתצוגה:
Kotlin
override fun onPopulateAccessibilityEvent(event: AccessibilityEvent?) { super.onPopulateAccessibilityEvent(event) // Call the super implementation to populate its text for the // event. Then, add text not present in a super class. // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { event?.text?.add(text) } } override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) { super.onInitializeAccessibilityEvent(event) // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event?.isChecked = isChecked() } override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info?.isCheckable = true info?.isChecked = isChecked() // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { info?.text = text } }
Java
@Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { super.onPopulateAccessibilityEvent(event); // Call the super implementation to populate its text for the // event. Then, add the text not present in a super class. // You typically only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { event.getText().add(text); } } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event.setChecked(isChecked()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info.setCheckable(true); info.setChecked(isChecked()); // You typically only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { info.setText(text); } }
אפשר להטמיע את השיטות האלה ישירות במחלקה של התצוגה המותאמת אישית.
הוספת הקשר מותאם אישית בנושא נגישות
שירותי נגישות יכולים לבדוק את היררכיית התצוגות שמכילה רכיב בממשק משתמש שיוצר אירוע נגישות. כך שירותי הנגישות יכולים לספק מידע הקשרי עשיר יותר שיעזור למשתמשים.
יש מקרים שבהם שירותי הנגישות לא יכולים לקבל מספיק מידע מהיררכיית התצוגה. דוגמה לכך היא אמצעי בקרה בממשק בהתאמה אישית שיש לו שתי אזורים או יותר שאפשר ללחוץ עליהם בנפרד, כמו אמצעי בקרה של יומן. במקרה הזה, השירותים לא יכולים לקבל מידע מספיק כי חלקי המשנה שאפשר ללחוץ עליהם לא נכללים בהיררכיית התצוגה.
איור 1. תצוגת יומן בהתאמה אישית עם רכיבי יום שאפשר לבחור.
בדוגמה באיור 1, היומן כולו מיושם כתצוגה אחת, ולכן שירותי הנגישות לא מקבלים מספיק מידע על התוכן של התצוגה ועל הבחירה של המשתמש בתוך התצוגה, אלא אם המפתח מספק מידע נוסף. לדוגמה, אם משתמש לוחץ על היום עם התווית 17, מסגרת הנגישות מקבלת רק את פרטי התיאור של כל פקד היומן. במקרה הזה, שירות הנגישות TalkBack מכריז על 'יומן' או 'יומן אפריל', והמשתמש לא יודע איזה יום נבחר.
כדי לספק מידע הקשר הולם לשירותי נגישות במצבים כאלה, אפשר להשתמש במסגרת כדי לציין היררכיית תצוגה וירטואלית. היררכיית תצוגה וירטואלית היא דרך שבה מפתחי אפליקציות יכולים לספק היררכיית תצוגה משלימה לשירותי נגישות, שתואמת יותר למידע שמוצג במסך. הגישה הזו מאפשרת לשירותי נגישות לספק למשתמשים מידע הקשרי מועיל יותר.
מצב נוסף שבו עשוי להיות צורך בהיררכיית תצוגה וירטואלית הוא ממשק משתמש שמכיל קבוצה של View רכיבי בקרה עם פונקציות שקשורות זו לזו, שבהן פעולה ברכיב בקרה אחד משפיעה על התוכן של רכיב אחד או יותר – למשל, בורר מספרים עם לחצני הגדלה והקטנה נפרדים. במקרה כזה, שירותי הנגישות לא יכולים לקבל מידע מספיק כי פעולה על אמצעי בקרה אחד משנה את התוכן באחר, והקשר בין אמצעי הבקרה האלה לא ברור לשירות.
כדי לטפל במצב הזה, צריך לקבץ את אמצעי הבקרה הרלוונטיים באמצעות תצוגה מכילה, ולספק היררכיית תצוגה וירטואלית מהמאגר הזה כדי להציג בבירור את המידע וההתנהגות שמספקים אמצעי הבקרה.
כדי לספק היררכיית תצוגה וירטואלית לתצוגה, מחליפים את השיטה
getAccessibilityNodeProvider()
בתצוגה המותאמת אישית או בקבוצת התצוגה ומחזירים הטמעה של
AccessibilityNodeProvider.
אפשר להטמיע היררכיית תצוגה וירטואלית באמצעות ספריית התמיכה עם השיטה ViewCompat.getAccessibilityNodeProvider()
ולספק הטמעה עם AccessibilityNodeProviderCompat.
כדי לפשט את המשימה של אספקת מידע לשירותי נגישות וניהול של מיקוד הנגישות, אפשר במקום זאת להטמיע את ExploreByTouchHelper.
הוא מספק AccessibilityNodeProviderCompat ואפשר לצרף אותו כ-AccessibilityDelegateCompat של תצוגה באמצעות הקריאה ל-setAccessibilityDelegate.
דוגמה מופיעה במאמר ExploreByTouchHelperActivity.
ExploreByTouchHelper משמש גם בווידג'טים של framework כמו CalendarView, דרך תצוגת הצאצא שלו SimpleMonthView.
טיפול באירועי מגע מותאמים אישית
יכול להיות שפקדים של תצוגה בהתאמה אישית ידרשו התנהגות לא סטנדרטית של אירועי מגע, כמו בדוגמאות הבאות.
הגדרת פעולות שמבוססות על קליקים
אם הווידג'ט שלכם משתמש בממשק OnClickListener או OnLongClickListener, המערכת מטפלת בפעולות ACTION_CLICK ו-ACTION_LONG_CLICK בשבילכם. אם האפליקציה שלכם משתמשת בווידג'ט מותאם אישית יותר שמסתמך על הממשק OnTouchListener, צריך להגדיר אמצעי טיפול מותאמים אישית בפעולות הנגישות שמבוססות על קליקים. כדי לעשות את זה, צריך להפעיל את המתודה replaceAccessibilityAction() לכל פעולה, כמו שמוצג בקטע הקוד הבא:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Assumes that the widget is designed to select text when tapped, and selects // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select) ) { view, commandArguments -> selectText() } ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all) ) { view, commandArguments -> selectAllText() } }
Java
@Override protected void onCreate(Bundle savedInstanceState) { ... // Assumes that the widget is designed to select text when tapped, and select // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select), (view, commandArguments) -> selectText()); ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all), (view, commandArguments) -> selectAllText()); }
יצירת אירועי קליקים מותאמים אישית
פקד בהתאמה אישית יכול להשתמש בonTouchEvent(MotionEvent)שיטת listener כדי לזהות את האירועים ACTION_DOWN ו-ACTION_UP ולהפעיל אירוע מסוג קליק מיוחד. כדי לשמור על תאימות לשירותי נגישות, הקוד שמטפל באירוע מסוג קליק המותאם הזה צריך לבצע את הפעולות הבאות:
כדי לטפל בדרישות האלה בצורה יעילה, הקוד צריך לבטל את ה-method performClick(), שצריך לקרוא להטמעה של ה-super של ה-method הזה ואז לבצע את כל הפעולות שנדרשות על ידי אירוע מסוג קליק. כשמזהים את פעולת הלחיצה המותאמת אישית, הקוד הזה צריך לקרוא לשיטה performClick(). בדוגמה הבאה של קוד אפשר לראות את התבנית הזו.
Kotlin
class CustomTouchView(context: Context) : View(context) { var downTouch = false override fun onTouchEvent(event: MotionEvent): Boolean { super.onTouchEvent(event) // Listening for the down and up touch events. return when (event.action) { MotionEvent.ACTION_DOWN -> { downTouch = true true } MotionEvent.ACTION_UP -> if (downTouch) { downTouch = false performClick() // Call this method to handle the response and // enable accessibility services to // perform this action for a user who can't // tap the touchscreen. true } else { false } else -> false // Return false for other touch events. } } override fun performClick(): Boolean { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any. super.performClick() // Handle the action for the custom click here. return true } }
Java
class CustomTouchView extends View { public CustomTouchView(Context context) { super(context); } boolean downTouch = false; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); // Listening for the down and up touch events switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downTouch = true; return true; case MotionEvent.ACTION_UP: if (downTouch) { downTouch = false; performClick(); // Call this method to handle the response and // enable accessibility services to // perform this action for a user who can't // tap the touchscreen. return true; } } return false; // Return false for other touch events. } @Override public boolean performClick() { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any. super.performClick(); // Handle the action for the custom click here. return true; } }
התבנית הקודמת עוזרת לוודא שאירוע מסוג קליק מותאם אישית תואם לשירותי נגישות. לשם כך, משתמשים בשיטה performClick() כדי ליצור אירוע נגישות ולספק נקודת כניסה לשירותי נגישות כדי לפעול בשם משתמש שמבצע את אירוע מסוג קליק מותאם אישית.