שירות נגישות הוא אפליקציה שמשפרת את ממשק המשתמש כדי לסייע משתמשים עם מוגבלויות או אשר באופן זמני לא יכולים לבצע אינטראקציה מלאה באמצעות מכשיר. לדוגמה, משתמשים שנוהגים ומטפלים בילד או ילדה, ייתכן שיהיה צורך בממשק נוסף או חלופי, או השתתפות במסיבה רועשת מאוד משוב.
Android מספק שירותי נגישות רגילים, כולל TalkBack , והמפתחים יכולים ליצור ולהפיץ את השירותים שלהם. המסמך הזה הסבר על העקרונות הבסיסיים של שירות נגישות.
אפשר לקבץ שירות נגישות עם אפליקציה רגילה או ליצור אותו בתור לפרויקט Android נפרד. השלבים ליצירת השירות זהים ב- בכל מצב.
יצירת שירות נגישות
בתוך הפרויקט, יוצרים מחלקה שמתרחבת
AccessibilityService
:
Kotlin
package com.example.android.apis.accessibility import android.accessibilityservice.AccessibilityService import android.view.accessibility.AccessibilityEvent class MyAccessibilityService : AccessibilityService() { ... override fun onInterrupt() {} override fun onAccessibilityEvent(event: AccessibilityEvent?) {} ... }
Java
package com.example.android.apis.accessibility; import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; public class MyAccessibilityService extends AccessibilityService { ... @Override public void onAccessibilityEvent(AccessibilityEvent event) { } @Override public void onInterrupt() { } ... }
אם יוצרים פרויקט חדש בשביל הService
הזה ואין לך כוונה להפעיל אפליקציה
שמשויכים אליו, אפשר להסיר את הכיתה Activity
עם הסימן לתחילת פעולה
מקור.
הצהרות והרשאות במניפסט
אפליקציות שמספקות שירותי נגישות חייבות לכלול הצהרות ספציפיות את קובצי המניפסט של האפליקציה שלהם כך שיטופלו כשירות נגישות על ידי Android המערכת. בקטע הזה מוסבר על ההגדרות הנדרשות והאופציונליות של שירותי נגישות.
הצהרה על שירות נגישות
כדי שהאפליקציה שלך תיחשב כשירות נגישות, יש לכלול service
רכיב - ולא רכיב activity
- בתוך application
במניפסט. בנוסף, בתוך הרכיב service
, יש לכלול
מסנן Intent של שירות נגישות. המניפסט צריך גם להגן על השירות
על ידי הוספת
BIND_ACCESSIBILITY_SERVICE
כדי להבטיח שרק המערכת תוכל לקשר אליו. הנה דוגמה:
<application> <service android:name=".MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:label="@string/accessibility_service_label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> </service> </application>
הגדרה של שירותי נגישות
שירותי הנגישות חייבים לספק תצורה שמציינת את הסוגים של
אירועי נגישות שבהם השירות מטפל ומידע נוסף על
את השירות. התצורה של שירות נגישות כלולה
AccessibilityServiceInfo
בכיתה. השירות יכול ליצור ולקבוע הגדרות אישיות באמצעות מופע של
כיתה
setServiceInfo()
בזמן ריצה. אבל לא כל אפשרויות ההגדרה זמינות כשמשתמשים
.
אפשר לכלול רכיב <meta-data>
במניפסט עם הפניה אל
קובץ תצורה, שמאפשר להגדיר את כל טווח האפשרויות
שירות נגישות, כמו בדוגמה הבאה:
<service android:name=".MyAccessibilityService"> ... <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
רכיב <meta-data>
הזה מתייחס לקובץ XML שיצרת
ספריית המשאבים של האפליקציה:
<project_dir>/res/xml/accessibility_service_config.xml>
. את הקוד הבא
מציג דוגמה לתוכן של קובץ תצורת השירות:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_description" android:packageNames="com.example.android.apis" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagDefault" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:canRetrieveWindowContent="true" android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" />
למידע נוסף על מאפייני XML שניתן להשתמש בהם ב קובץ התצורה של שירותי נגישות. אפשר לעיין בחומר העזר הבא: תיעוד:
android:description
android:packageNames
android:accessibilityEventTypes
android:accessibilityFlags
android:accessibilityFeedbackType
android:notificationTimeout
android:canRetrieveWindowContent
android:settingsActivity
מידע נוסף על הגדרות התצורה שניתן להגדיר באופן דינמי
בזמן הריצה,
AccessibilityServiceInfo
מסמכי עזר.
הגדרת שירות הנגישות
כדאי להביא בחשבון את הנקודות הבאות כשמגדירים את משתני ההגדרה של שירות נגישות כדי לומר למערכת איך ומתי להפעיל:
- לאילו סוגי אירועים היית רוצה שהאירוע יגיב?
- האם השירות צריך להיות פעיל בכל האפליקציות או רק בחבילה ספציפית שמות?
- באילו סוגי משובים הוא משתמש?
יש שתי אפשרויות להגדרת המשתנים האלה. אפשרות של תאימות לאחור
הוא להגדיר אותם בקוד,
setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
כדי לעשות את זה, משנים את
onServiceConnected()
ולהגדיר שם את השירות, כפי שמוצג בדוגמה הבאה:
Kotlin
override fun onServiceConnected() { info.apply { // Set the type of events that this service wants to listen to. Others // aren't passed to this service. eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED // If you only want this service to work with specific apps, set their // package names here. Otherwise, when the service is activated, it // listens to events from all apps. packageNames = arrayOf("com.example.android.myFirstApp", "com.example.android.mySecondApp") // Set the type of feedback your service provides. feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN // Default services are invoked only if no package-specific services are // present for the type of AccessibilityEvent generated. This service is // app-specific, so the flag isn't necessary. For a general-purpose // service, consider setting the DEFAULT flag. // flags = AccessibilityServiceInfo.DEFAULT; notificationTimeout = 100 } this.serviceInfo = info }
Java
@Override public void onServiceConnected() { // Set the type of events that this service wants to listen to. Others // aren't passed to this service. info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED; // If you only want this service to work with specific apps, set their // package names here. Otherwise, when the service is activated, it listens // to events from all apps. info.packageNames = new String[] {"com.example.android.myFirstApp", "com.example.android.mySecondApp"}; // Set the type of feedback your service provides. info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; // Default services are invoked only if no package-specific services are // present for the type of AccessibilityEvent generated. This service is // app-specific, so the flag isn't necessary. For a general-purpose service, // consider setting the DEFAULT flag. // info.flags = AccessibilityServiceInfo.DEFAULT; info.notificationTimeout = 100; this.setServiceInfo(info); }
האפשרות השנייה היא להגדיר את השירות באמצעות קובץ XML. מסוימת
להגדרות אישיות, כמו
canRetrieveWindowContent
זמינים רק אם הגדרת את השירות באמצעות XML. ההגדרות האישיות
מהדוגמה הקודמת נראות כך כאשר הן מוגדרות באמצעות XML:
<accessibility-service android:accessibilityEventTypes="typeViewClicked|typeViewFocused" android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity" android:canRetrieveWindowContent="true" />
אם אתם משתמשים ב-XML, יש להפנות אליו במניפסט על ידי הוספת
<meta-data>
הצהרת שירות שמצביעה על קובץ ה-XML. אם אתם מאחסנים את קובץ ה-XML ב:
res/xml/serviceconfig.xml
, התג החדש נראה כך:
<service android:name=".MyAccessibilityService"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/serviceconfig" /> </service>
שיטות שירות הנגישות
שירות נגישות צריך להרחיב את המחלקה AccessibilityService
לשנות את השיטות הבאות מאותה המחלקה. השיטות האלה מוצגות
הסדר שבו מערכת Android מתקשרת אליהם: מרגע הפעלת השירות
(onServiceConnected()
), עד שהאפליקציה פועלת
(onAccessibilityEvent()
,
onInterrupt()
),
עד למועד כיבוי
(onUnbind()
).
onServiceConnected()
: (אופציונלי) המערכת קוראת לשיטה הזו כשהיא מתחבר לשירות הנגישות שלך. ניתן להשתמש בשיטה הזו לביצוע הגדרה חד-פעמית שלבים לשירות שלך, כולל התחברות למערכת המשוב של המשתמשים שירותים, כגון מנהל האודיו או הרטט של המכשיר. אם רוצים להגדיר את תצורת השירות בזמן הריצה או לבצע שינויים חד-פעמיים, זהו מיקום נוח להתקשר אלsetServiceInfo()
.onAccessibilityEvent()
: (חובה) המערכת קוראת חזרה לשיטה הזו כאשר הוא מזההAccessibilityEvent
שתואם לפרמטרים לסינון אירועים שצוינו על ידי הנגישות שלך השירות, למשל כשהמשתמש מקיש על לחצן או מתמקד בממשק משתמש באפליקציה ששירות הנגישות שלך מספק משוב עליה. מתי המערכת קוראת לשיטה הזו, היא מעבירה אתAccessibilityEvent
המשויך, שהשירות יכול לפרש ולהשתמש בו כדי לספק משוב משתמש. אפשר לקרוא לשיטה הזו פעמים רבות במהלך מחזור החיים של לאחר השיפור.onInterrupt()
: (חובה) המערכת קוראת לשיטה הזו כשהמערכת רוצה להפסיק את המשוב שהשירות מספק, בדרך כלל תגובה לפעולת משתמש, כמו העברת המיקוד לפקד אחר. הזה ניתן לקרוא ל-method מספר פעמים במהלך מחזור החיים של השירות.onUnbind()
: (אופציונלי) המערכת קוראת לשיטה הזו כשהמערכת עומד לכבות את שירות הנגישות. בשיטה הזו אפשר הליכי השבתה חד-פעמיים, כולל ביטול הקצאת משוב למשתמשים שירותים, כגון מנהל האודיו או הרטט של המכשיר.
השיטות האלה להתקשרות חזרה מספקות את המבנה הבסיסי של הנגישות
לאחר השיפור. באפשרותך להחליט איך לעבד את הנתונים שמסופקים על ידי מערכת Android ב
בצורה של אובייקטים מסוג AccessibilityEvent
ונותנים משוב למשתמש. עבור
מידע נוסף על קבלת מידע מאירוע נגישות, ראו קבלת
פרטי האירוע.
הרשמה לאירועי נגישות
אחת הפונקציות החשובות ביותר בהגדרת שירות הנגישות היא מאפשרת לציין אילו סוגים של אירועי נגישות השירות לטפל בתופעה. ציון המידע הזה מאפשר לשירותי הנגישות לשתף פעולה שתי רשתות נוירונים זו מול זו, ונותן לכם את הגמישות לטפל רק באירוע ספציפי מאפליקציות ספציפיות. סינון האירועים יכול לכלול את הפרטים הבאים: קריטריונים:
שמות חבילות: מציינים את שמות החבילות של אפליקציות שהנגישות שלהן האירועים שבהם אתם רוצים שהשירות יטפל. אם לא תשמיט את הפרמטר הזה, שירות נגישות נחשב כזמין לנגישות של שירות אירועים באפליקציה כלשהי. אפשר להגדיר את הפרמטר הזה בשירות הנגישות קובצי תצורה עם המאפיין
android:packageNames
רשימה מופרדת בפסיקים או להשתמש בפונקציהAccessibilityServiceInfo.packageNames
חבר.סוגי אירועים: מציינים את הסוגים של אירועי הנגישות שאתם רוצים שירות שצריך לטפל בו. אפשר להגדיר את הפרמטר הזה בשירות הנגישות קובצי תצורה עם המאפיין
android:accessibilityEventTypes
: רשימה שמופרדת באמצעות התו|
, לדוגמהaccessibilityEventTypes="typeViewClicked|typeViewFocused"
. לחלופין אפשר להגדיר באמצעות התכונהAccessibilityServiceInfo.eventTypes
חבר.
כשמגדירים את שירות הנגישות, חשוב לשקול אילו אירועים יכול לטפל באירועים האלה ולהירשם אליהם בלבד. מאחר שמשתמשים יכולים להפעיל מספר שירותי נגישות בו-זמנית, אין לצרוך את השירות אירועים שהוא לא יכול לטפל בהם. חשוב לזכור ששירותים אחרים עשויים לטפל בהן אירועים כדי לשפר את חוויית המשתמש.
עוצמת הקול לנגישות
מכשירים שפועלת בהם גרסת Android 8.0 (רמת API 26) ואילך כוללים את
STREAM_ACCESSIBILITY
קטגוריית עוצמת קול, שמאפשרת לשלוט בעוצמת הקול של הנגישות
פלט האודיו של השירות בנפרד מצלילים אחרים במכשיר.
שירותי הנגישות יכולים להשתמש בסוג השידור הזה על ידי הגדרת
FLAG_ENABLE_ACCESSIBILITY_VOLUME
כאפשרות. לאחר מכן אפשר לשנות את עוצמת הקול של האודיו לנגישות במכשיר באמצעות התקשרות
ה
adjustStreamVolume()
ב-method batch במופע של המכשיר
AudioManager
קטע הקוד הבא מדגים איך שירות נגישות יכול להשתמש
קטגוריית כרך STREAM_ACCESSIBILITY
:
Kotlin
import android.media.AudioManager.* class MyAccessibilityService : AccessibilityService() { private val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager override fun onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) { if (accessibilityEvent.source.text == "Increase volume") { audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, ADJUST_RAISE, 0) } } }
Java
import static android.media.AudioManager.*; public class MyAccessibilityService extends AccessibilityService { private AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); @Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { AccessibilityNodeInfo interactedNodeInfo = accessibilityEvent.getSource(); if (interactedNodeInfo.getText().equals("Increase volume")) { audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, ADJUST_RAISE, 0); } } }
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 6:35.
קיצור הדרך לנגישות
במכשירים עם Android מגרסה 8.0 (רמת API 26) ואילך, המשתמשים יכולים להפעיל ולהשבית את שירות הנגישות המועדף בכל מסך על ידי לחיצה על לחיצה על שני מקשי עוצמת הקול בו-זמנית. למרות שקיצור דרך זה מאפשר משבית את Talkback כברירת מחדל, המשתמשים יכולים להגדיר את הלחצן כדי להפעיל להשבית כל שירות שמותקן במכשיר שלהם.
כדי שמשתמשים יוכלו לגשת לשירות נגישות מסוים מקש קיצור, השירות צריך לבקש את התכונה בזמן הריצה.
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 13:25.
לחצן הנגישות
במכשירים עם אזור ניווט שעבר עיבוד באמצעות תוכנה שפועלת בהם מערכת Android 8.0 (רמת API 26) ומעלה, הצד השמאלי של סרגל הניווט כולל לחצן הנגישות. כשמשתמשים ילחצו על הלחצן הזה, הם יוכלו להפעיל אחת מספר תכונות ושירותים מופעלים של נגישות, בהתאם לתוכן שמוצגת עכשיו במסך.
כדי לאפשר למשתמשים להפעיל שירות נגישות נתון באמצעות הנגישות
לחצן, השירות צריך להוסיף את
FLAG_REQUEST_ACCESSIBILITY_BUTTON
דגל ב-android:accessibilityFlags
של אובייקט AccessibilityServiceInfo
. לאחר מכן השירות יכול לרשום התקשרות חזרה באמצעות
registerAccessibilityButtonCallback()
קטע הקוד הבא מדגים איך אפשר להגדיר נגישות שירות לתגובה למשתמש בלחיצה על לחצן הנגישות:
Kotlin
private var mAccessibilityButtonController: AccessibilityButtonController? = null private var accessibilityButtonCallback: AccessibilityButtonController.AccessibilityButtonCallback? = null private var mIsAccessibilityButtonAvailable: Boolean = false override fun onServiceConnected() { mAccessibilityButtonController = accessibilityButtonController mIsAccessibilityButtonAvailable = mAccessibilityButtonController?.isAccessibilityButtonAvailable ?: false if (!mIsAccessibilityButtonAvailable) return serviceInfo = serviceInfo.apply { flags = flags or AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON } accessibilityButtonCallback = object : AccessibilityButtonController.AccessibilityButtonCallback() { override fun onClicked(controller: AccessibilityButtonController) { Log.d("MY_APP_TAG", "Accessibility button pressed!") // Add custom logic for a service to react to the // accessibility button being pressed. } override fun onAvailabilityChanged( controller: AccessibilityButtonController, available: Boolean ) { if (controller == mAccessibilityButtonController) { mIsAccessibilityButtonAvailable = available } } } accessibilityButtonCallback?.also { mAccessibilityButtonController?.registerAccessibilityButtonCallback(it, null) } }
Java
private AccessibilityButtonController accessibilityButtonController; private AccessibilityButtonController .AccessibilityButtonCallback accessibilityButtonCallback; private boolean mIsAccessibilityButtonAvailable; @Override protected void onServiceConnected() { accessibilityButtonController = getAccessibilityButtonController(); mIsAccessibilityButtonAvailable = accessibilityButtonController.isAccessibilityButtonAvailable(); if (!mIsAccessibilityButtonAvailable) { return; } AccessibilityServiceInfo serviceInfo = getServiceInfo(); serviceInfo.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; setServiceInfo(serviceInfo); accessibilityButtonCallback = new AccessibilityButtonController.AccessibilityButtonCallback() { @Override public void onClicked(AccessibilityButtonController controller) { Log.d("MY_APP_TAG", "Accessibility button pressed!"); // Add custom logic for a service to react to the // accessibility button being pressed. } @Override public void onAvailabilityChanged( AccessibilityButtonController controller, boolean available) { if (controller.equals(accessibilityButtonController)) { mIsAccessibilityButtonAvailable = available; } } }; if (accessibilityButtonCallback != null) { accessibilityButtonController.registerAccessibilityButtonCallback( accessibilityButtonCallback, null); } }
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 16:28.
תנועות של טביעות אצבעות
שירותי נגישות במכשירים שמותקנת בהם גרסת Android 8.0 (רמת API 26) ומעלה יכול להגיב להחלקות בכיוון מסוים (למעלה, למטה, שמאלה וימינה) לאורך מחיישן טביעות אצבע. כדי להגדיר שירות שיקבל שיחות חוזרות לגביו ויוצרות את הרצף הבא:
- להצהיר על
USE_BIOMETRIC
ואת ההרשאהCAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES
ליכולות של בינה מלאכותית גנרטיבית. - מגדירים את
FLAG_REQUEST_FINGERPRINT_GESTURES
בתוך המאפייןandroid:accessibilityFlags
. - רישום להתקשרות חזרה באמצעות
registerFingerprintGestureCallback()
.
חשוב לזכור שלא בכל המכשירים יש חיישני טביעות אצבע. כדי לזהות
אם המכשיר תומך בחיישן,
isHardwareDetected()
. גם במכשיר עם חיישן טביעות אצבע, השירות לא יוכל
להשתמש בחיישן כשהוא בשימוש למטרות אימות. כדי לזהות מתי
שהחיישן זמין,
isGestureDetectionAvailable()
וליישם את
onGestureDetectionAvailabilityChanged()
קריאה חוזרת.
בקטע הקוד הבא מוצגת דוגמה לשימוש בתנועות של טביעות אצבע לנווט בלוח משחק וירטואלי:
// AndroidManifest.xml <manifest ... > <uses-permission android:name="android.permission.USE_FINGERPRINT" /> ... <application> <service android:name="com.example.MyFingerprintGestureService" ... > <meta-data android:name="android.accessibilityservice" android:resource="@xml/myfingerprintgestureservice" /> </service> </application> </manifest>
// myfingerprintgestureservice.xml <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" ... android:accessibilityFlags=" ... |flagRequestFingerprintGestures" android:canRequestFingerprintGestures="true" ... />
Kotlin
// MyFingerprintGestureService.kt import android.accessibilityservice.FingerprintGestureController.* class MyFingerprintGestureService : AccessibilityService() { private var gestureController: FingerprintGestureController? = null private var fingerprintGestureCallback: FingerprintGestureController.FingerprintGestureCallback? = null private var mIsGestureDetectionAvailable: Boolean = false override fun onCreate() { gestureController = fingerprintGestureController mIsGestureDetectionAvailable = gestureController?.isGestureDetectionAvailable ?: false } override fun onServiceConnected() { if (mFingerprintGestureCallback != null || !mIsGestureDetectionAvailable) return fingerprintGestureCallback = object : FingerprintGestureController.FingerprintGestureCallback() { override fun onGestureDetected(gesture: Int) { when (gesture) { FINGERPRINT_GESTURE_SWIPE_DOWN -> moveGameCursorDown() FINGERPRINT_GESTURE_SWIPE_LEFT -> moveGameCursorLeft() FINGERPRINT_GESTURE_SWIPE_RIGHT -> moveGameCursorRight() FINGERPRINT_GESTURE_SWIPE_UP -> moveGameCursorUp() else -> Log.e(MY_APP_TAG, "Error: Unknown gesture type detected!") } } override fun onGestureDetectionAvailabilityChanged(available: Boolean) { mIsGestureDetectionAvailable = available } } fingerprintGestureCallback?.also { gestureController?.registerFingerprintGestureCallback(it, null) } } }
Java
// MyFingerprintGestureService.java import static android.accessibilityservice.FingerprintGestureController.*; public class MyFingerprintGestureService extends AccessibilityService { private FingerprintGestureController gestureController; private FingerprintGestureController .FingerprintGestureCallback fingerprintGestureCallback; private boolean mIsGestureDetectionAvailable; @Override public void onCreate() { gestureController = getFingerprintGestureController(); mIsGestureDetectionAvailable = gestureController.isGestureDetectionAvailable(); } @Override protected void onServiceConnected() { if (fingerprintGestureCallback != null || !mIsGestureDetectionAvailable) { return; } fingerprintGestureCallback = new FingerprintGestureController.FingerprintGestureCallback() { @Override public void onGestureDetected(int gesture) { switch (gesture) { case FINGERPRINT_GESTURE_SWIPE_DOWN: moveGameCursorDown(); break; case FINGERPRINT_GESTURE_SWIPE_LEFT: moveGameCursorLeft(); break; case FINGERPRINT_GESTURE_SWIPE_RIGHT: moveGameCursorRight(); break; case FINGERPRINT_GESTURE_SWIPE_UP: moveGameCursorUp(); break; default: Log.e(MY_APP_TAG, "Error: Unknown gesture type detected!"); break; } } @Override public void onGestureDetectionAvailabilityChanged(boolean available) { mIsGestureDetectionAvailable = available; } }; if (fingerprintGestureCallback != null) { gestureController.registerFingerprintGestureCallback( fingerprintGestureCallback, null); } } }
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 9:03.
המרת טקסט לדיבור בכמה שפות
החל מ-Android 8.0 (רמת API 26), שירות המרת טקסט לדיבור (TTS) של Android
יכולים לזהות ולדבר ביטויים במספר שפות בתוך בלוק אחד של
טקסט. כדי להפעיל את היכולת הזו למעבר אוטומטי בין שפות בנגישות
שירות, לאחסן את כל המחרוזות
LocaleSpan
אובייקטים, כפי שמוצג
בקטע הקוד הבא:
Kotlin
val localeWrappedTextView = findViewById<TextView>(R.id.my_french_greeting_text).apply { text = wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE) } private fun wrapTextInLocaleSpan(originalText: CharSequence, loc: Locale): SpannableStringBuilder { return SpannableStringBuilder(originalText).apply { setSpan(LocaleSpan(loc), 0, originalText.length - 1, 0) } }
Java
TextView localeWrappedTextView = findViewById(R.id.my_french_greeting_text); localeWrappedTextView.setText(wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE)); private SpannableStringBuilder wrapTextInLocaleSpan( CharSequence originalText, Locale loc) { SpannableStringBuilder myLocaleBuilder = new SpannableStringBuilder(originalText); myLocaleBuilder.setSpan(new LocaleSpan(loc), 0, originalText.length() - 1, 0); return myLocaleBuilder; }
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 10:59.
פעולה בשם משתמשים
החל משנת 2011, שירותי נגישות יכולים לפעול בשם משתמשים, כולל. שינוי מיקוד הקלט ובחירה (הפעלה) של רכיבים בממשק המשתמש. לחשבון בשנת 2012, מגוון הפעולות הורחב וכולל רשימות גלילה ואינטראקציות עם שדות טקסט. שירותי הנגישות יכולים גם לבצע פעולות גלובליות, כמו לנווט למסך הבית, ללחוץ על הלחצן 'הקודם', ולפתוח את מסך ההתראות ורשימת האפליקציות האחרונות. מאז 2012, Android כולל התמקדות בנגישות, שמאפשרת לבחור את כל הרכיבים הגלויים שירות נגישות.
היכולות האלה מאפשרות למפתחים של שירותי נגישות ליצור מצבי ניווט, כמו ניווט באמצעות תנועות, ומתן אפשרויות למשתמשים עם מוגבלויות שליטה משופרת במכשירים מבוססי Android שלהם.
האזנה לתנועות
שירותי הנגישות יכולים להקשיב לתנועות ספציפיות ולהגיב על ידי הפעלה
מטעמו של משתמש. כדי להשתמש בתכונה הזו צריך לבקש את שירות הנגישות שלך
הפעלת התכונה 'גילוי באמצעות מגע'. השירות שלך יכול לבקש זאת
הפעלה באמצעות הגדרה של
flags
במכונה AccessibilityServiceInfo
של השירות,
FLAG_REQUEST_TOUCH_EXPLORATION_MODE
,
כפי שאפשר לראות בדוגמה הבאה.
Kotlin
class MyAccessibilityService : AccessibilityService() { override fun onCreate() { serviceInfo.flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE } ... }
Java
public class MyAccessibilityService extends AccessibilityService { @Override public void onCreate() { getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; } ... }
לאחר קבלת בקשה לשירות להפעלת 'גילוי באמצעות מגע', המשתמש צריך לאפשר
להפעיל את התכונה, אם היא עדיין לא פעילה. כשהתכונה הזו
פעיל, השירות שלך מקבל התראה על תנועות נגישות באמצעות
של השירות שלך
onGesture()
שיטת הקריאה החוזרת (callback) ויכולה להגיב על ידי פעולה בשם המשתמש.
תנועות מתמשכות
מכשירים שפועלת בהם גרסת Android 8.0 (רמת API 26) תומכים בתנועות המשך, או
תנועות פרוגרמטיות שמכילות יותר
אובייקט Path
.
כשמציינים רצף של תנועות, אפשר לציין שהן שייכות
אותה תנועה פרוגרמטית באמצעות הארגומנט הסופי willContinue
GestureDescription.StrokeDescription
constructor, כפי שמוצג בקטע הקוד הבא:
Kotlin
// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down. private fun doRightThenDownDrag() { val dragRightPath = Path().apply { moveTo(200f, 200f) lineTo(400f, 200f) } val dragRightDuration = 500L // 0.5 second // The starting point of the second path must match // the ending point of the first path. val dragDownPath = Path().apply { moveTo(400f, 200f) lineTo(400f, 400f) } val dragDownDuration = 500L val rightThenDownDrag = GestureDescription.StrokeDescription( dragRightPath, 0L, dragRightDuration, true ).apply { continueStroke(dragDownPath, dragRightDuration, dragDownDuration, false) } }
Java
// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down. private void doRightThenDownDrag() { Path dragRightPath = new Path(); dragRightPath.moveTo(200, 200); dragRightPath.lineTo(400, 200); long dragRightDuration = 500L; // 0.5 second // The starting point of the second path must match // the ending point of the first path. Path dragDownPath = new Path(); dragDownPath.moveTo(400, 200); dragDownPath.lineTo(400, 400); long dragDownDuration = 500L; GestureDescription.StrokeDescription rightThenDownDrag = new GestureDescription.StrokeDescription(dragRightPath, 0L, dragRightDuration, true); rightThenDownDrag.continueStroke(dragDownPath, dragRightDuration, dragDownDuration, false); }
למידע נוסף, ראו את הסרטון מה חדש בנגישות ב-Android מ-Google I/O 2017. החל מ- 15:47.
שימוש בפעולות בנושא נגישות
שירותי הנגישות יכולים לפעול בשם המשתמשים כדי לפשט את האינטראקציה עם אפליקציות ופרודוקטיביות יותר. היכולת של שירותי הנגישות נוספו ב-2011 והורחבו משמעותית ב-2012.
כדי לפעול בשם משתמשים, שירות הנגישות צריך לרשום
כדי לקבל אירועים מאפליקציות ולבקש הרשאה לצפות בתוכן
מאפליקציות על ידי הגדרה של android:canRetrieveWindowContent
כ-true
קובץ תצורת שירות. כאשר אירועים מתקבלים
השירות, הוא יכול לאחזר את
AccessibilityNodeInfo
מהאירוע באמצעות
getSource()
באמצעות האובייקט AccessibilityNodeInfo
, השירות יכול לחקור את התצוגה
ההיררכיה כדי לקבוע איזו פעולה לבצע ואז לפעול עבור המשתמש באמצעות
performAction()
.
Kotlin
class MyAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { // Get the source node of the event. event.source?.apply { // Use the event and node information to determine what action to // take. // Act on behalf of the user. performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) // Recycle the nodeInfo object. recycle() } } ... }
Java
public class MyAccessibilityService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { // Get the source node of the event. AccessibilityNodeInfo nodeInfo = event.getSource(); // Use the event and node information to determine what action to take. // Act on behalf of the user. nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); // Recycle the nodeInfo object. nodeInfo.recycle(); } ... }
השיטה performAction()
מאפשרת לשירות לבצע פעולה בתוך
אפליקציה. אם השירות צריך לבצע פעולה גלובלית, כמו
לנווט למסך הבית, להקיש על הלחצן 'הקודם', או לפתוח את
במסך ההתראות או ברשימת האפליקציות האחרונות, ואז להשתמש
performGlobalAction()
.
שימוש בסוגי התמקדות
בשנת 2012, הושקו ב-Android התמקדות בממשק המשתמש שנקראת התמקדות בנגישות. שירותי הנגישות יכולים להשתמש במיקוד הזה כדי לבחור כל ממשק משתמש גלוי ולפעול לפיו. סוג המיקוד הזה שונה ממיקוד בקלט, קובע איזה רכיב בממשק המשתמש שמופיע במסך מקבל קלט מקיש על תווים, מקיש על Enter במקלדת או לוחץ על הלחצן המרכזי בלחצני החיצים (D-pad).
ייתכן מצב שבו רכיב אחד בממשק המשתמש יתמקד בקלט בזמן לרכיב אחר יש מיקוד בנגישות. המטרה של התמקדות בנגישות היא כדי לספק שירותי נגישות שיטה ליצירת אינטראקציה עם רכיבים במסך, בין אם ניתן להתמקד בקלט על ידי נקודת מבט של המערכת. כדי לעזור להבטיח ששירות הנגישות שלך מקיים אינטראקציה נכון עם האפליקציות רכיבי קלט, צריך לפעול לפי ההנחיות לבדיקת הפורמטים של האפליקציות נגישות כדי לבדוק את השירות בזמן השימוש באפליקציה רגילה.
שירות נגישות יכול לקבוע איזה רכיב בממשק המשתמש מכיל קלט
מיקוד או נגישות באמצעות
AccessibilityNodeInfo.findFocus()
. תוכלו גם לחפש אלמנטים שניתן לבחור עם התמקדות בקלט
באמצעות
focusSearch()
. לבסוף, שירות הנגישות יכול להגדיר את מיקוד הנגישות באמצעות
ה
performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS)
.
איסוף מידע
בשירותי נגישות יש שיטות סטנדרטיות לאיסוף וייצוג של מפתחות יחידות של פרטים שהמשתמשים סיפקו, כמו פרטי אירועים, טקסט ומספרים.
קבלת פרטים על השינויים בחלון
מערכת Android 9 (רמת API 28) ואילך מאפשרת לאפליקציות לעקוב אחרי עדכוני חלונות כאשר
אפליקציה משחזרת חלונות מרובים בו-זמנית. כאשר
TYPE_WINDOWS_CHANGED
של האירוע, השתמשו
getWindowChanges()
API לקביעת האופן שבו החלונות משתנים. במהלך עדכון של מספר חלונות, כל אחד
המערכת מפיקה סדרת אירועים משלו. ה-method getSource()
מחזירה את הרמה הבסיסית (root)
של החלון המשויך לכל אירוע.
אם באפליקציה מוגדרת חלונית נגישות
כותרים
View
אובייקטים, השירות שלכם יכול לזהות מתי
ממשק המשתמש של האפליקציה מעודכן. כאשר
TYPE_WINDOW_STATE_CHANGED
התרחש אירוע, יש להשתמש בסוגים שהוחזרו על ידי
getContentChangeTypes()
כדי לקבוע איך החלון משתנה. לדוגמה, ה-framework יכול לזהות מתי
לחלונית יש כותרת חדשה או כאשר חלונית נעלמת.
קבלת פרטי האירוע
מערכת Android מספקת מידע לשירותי נגישות על ממשק המשתמש
אינטראקציה באמצעות AccessibilityEvent
אובייקטים. בגרסאות קודמות של Android,
המידע הזמין עבור אירוע נגישות, תוך מתן חוויה
פרטים על שליטה בממשק המשתמש שנבחרה על ידי משתמשים, מוצעת באופן מוגבל
מידע לפי הקשר. במקרים רבים, המידע שחסר בהקשר עשוי להיות
חיוניות להבנת המשמעות של אמצעי הבקרה שנבחר.
דוגמה לממשק שבו ההקשר הוא קריטי היא יומן או יום תכנון. אם המשתמש בוחר משבצת זמן של 16:00 ברשימה של ימים שני עד שישי ושירות הנגישות מכריז "16:00", אבל לא מודיע על יום השבוע. השם, היום בחודש או שם החודש. המשוב שמתקבל מבלבל. במקרה זה, ההקשר של שליטה בממשק המשתמש הוא קריטי משתמש שרוצה לקבוע פגישה.
מאז 2011, Android מרחיב באופן משמעותי את כמות המידע שירות נגישות יכול לקבל מידע על אינטראקציה עם ממשק המשתמש באמצעות כתיבת אירועי נגישות על סמך היררכיית התצוגות. היררכיית תצוגות מפורטות היא קבוצה של רכיבי ממשק משתמש שמכילים את הרכיב (ההורים שלו) ואת המשתמש רכיבים בממשק שעשויים להיות נכללים על ידי הרכיב הזה (הצאצאים שלו). לחשבון כך, מערכת Android יכולה לספק פרטים עשירים יותר על אירועי נגישות, שירותי נגישות מספקים משוב שימושי יותר למשתמשים.
שירות נגישות מקבל מידע על אירוע בממשק משתמש דרך
AccessibilityEvent
שהועברה על-ידי המערכת אל השירות
שיטת קריאה חוזרת onAccessibilityEvent()
. האובייקט הזה מספק פרטים על
אירוע, כולל סוג האובייקט שעליו מתבצעת הפעולה, הטקסט התיאורי שלו
פרטים נוספים.
AccessibilityEvent.getRecordCount()
וגםgetRecord(int)
: השיטות האלה מאפשרות לאחזר אתAccessibilityRecord
אובייקטים שתורמים ל-AccessibilityEvent
שהועברו אליכם על ידי המערכת. רמת הפירוט הזו מספקת הקשר נוסף לגבי האירוע מפעיל את שירות הנגישות.AccessibilityRecord.getSource()
: השיטה הזו מחזירה אובייקטAccessibilityNodeInfo
. האובייקט הזה מאפשר לבקש את היררכיית הפריסה (הורים וצאצאים) של הרכיב מקור אירוע הנגישות. התכונה הזו מאפשרת לנגישות לבדוק את ההקשר המלא של האירוע, כולל התוכן מצב של כל תצוגה כוללת או צפיות צאצא.
פלטפורמת Android מספקת ל-AccessibilityService
את היכולת לבצע שאילתה
היררכיית התצוגות, תוך איסוף מידע על רכיב ממשק המשתמש שיוצר
וגם ההורה והצאצאים שלו. כדי לעשות את זה, צריך להגדיר את השורה הבאה.
בתצורת ה-XML:
android:canRetrieveWindowContent="true"
בסיום, מקבלים אובייקט AccessibilityNodeInfo
באמצעות getSource()
.
הקריאה הזו מחזירה אובייקט רק אם החלון שבו נוצר האירוע
עדיין החלון הפעיל. אחרת, היא מחזירה null, ולכן צריך להתנהג בהתאם.
בדוגמה הבאה, הקוד מבצע את הפעולות הבאות כשאירוע מתקבל:
- פעולה זו תופסת מיד את ההורה של התצוגה המפורטת שממנה הגיע האירוע.
- בתצוגה הזו, הוא מחפש תווית ותיבת סימון בצפיות של צאצא.
- אם הוא מוצא אותם, הוא יוצר מחרוזת לדיווח למשתמש, שמציינת את ואם היא סומנה.
אם בשלב כלשהו מוחזר ערך null כשעוברים את היררכיית התצוגות, השיטה מוותרת בשקט.
Kotlin
// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo. override fun onAccessibilityEvent(event: AccessibilityEvent) { val source: AccessibilityNodeInfo = event.source ?: return // Grab the parent of the view that fires the event. val rowNode: AccessibilityNodeInfo = getListItemNodeInfo(source) ?: return // Using this parent, get references to both child nodes, the label, and the // checkbox. val taskLabel: CharSequence = rowNode.getChild(0)?.text ?: run { rowNode.recycle() return } val isComplete: Boolean = rowNode.getChild(1)?.isChecked ?: run { rowNode.recycle() return } // Determine what the task is and whether it's complete based on the text // inside the label, and the state of the checkbox. if (rowNode.childCount < 2 || !rowNode.getChild(1).isCheckable) { rowNode.recycle() return } val completeStr: String = if (isComplete) { getString(R.string.checked) } else { getString(R.string.not_checked) } val reportStr = "$taskLabel$completeStr" speakToUser(reportStr) }
Java
// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo. @Override public void onAccessibilityEvent(AccessibilityEvent event) { AccessibilityNodeInfo source = event.getSource(); if (source == null) { return; } // Grab the parent of the view that fires the event. AccessibilityNodeInfo rowNode = getListItemNodeInfo(source); if (rowNode == null) { return; } // Using this parent, get references to both child nodes, the label, and the // checkbox. AccessibilityNodeInfo labelNode = rowNode.getChild(0); if (labelNode == null) { rowNode.recycle(); return; } AccessibilityNodeInfo completeNode = rowNode.getChild(1); if (completeNode == null) { rowNode.recycle(); return; } // Determine what the task is and whether it's complete based on the text // inside the label, and the state of the checkbox. if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) { rowNode.recycle(); return; } CharSequence taskLabel = labelNode.getText(); final boolean isComplete = completeNode.isChecked(); String completeStr = null; if (isComplete) { completeStr = getString(R.string.checked); } else { completeStr = getString(R.string.not_checked); } String reportStr = taskLabel + completeStr; speakToUser(reportStr); }
עכשיו יש לך שירות נגישות מלא ופעיל. אפשר לנסות להגדיר איך
הוא מקיים אינטראקציה עם המשתמש על ידי הוספת המרת טקסט לדיבור של Android
מנוע
או להשתמש ב-Vibrator
כדי לספק משוב פיזי
משוב.
עיבוד טקסט
מכשירים שפועלת בהם גרסת Android 8.0 (רמת API 26) ואילך כוללים תכונות עיבוד טקסט שמקלות על שירותי הנגישות לזהות יחידות טקסט ספציפיות שמופיעות על המסך ולפעול לפיהן.
הסברים קצרים
Android 9 (רמת API 28) כוללת כמה יכולות שמעניקות לכם גישה
הסברים קצרים בממשק המשתמש של האפליקציה. כדאי להשתמש
getTooltipText()
לקרוא את הטקסט של הסבר קצר, ולהשתמש
ACTION_SHOW_TOOLTIP
וגם
ACTION_HIDE_TOOLTIP
כדי להורות למופעים של View
להציג או להסתיר את ההסברים הקצרים שלהם.
טקסט של רמז
החל משנת 2017, ב-Android יש כמה שיטות לביצוע אינטראקציה עם טקסט רמז לאובייקט מבוסס-טקסט:
-
isShowingHintText()
וגםsetShowingHintText()
methods מציינות ומגדירות, בהתאמה, אם הטקסט הנוכחי של הצומת מייצג את טקסט הרמז של הצומת. getHintText()
מספקת גישה לטקסט הרמז עצמו. גם אם האובייקט לא מוצג טקסט רמז, הקריאה אלgetHintText()
מצליחה.
המיקומים של תווי הטקסט במסך
במכשירים בגרסת Android 8.0 (רמת API 26) ואילך, שירותי נגישות
יכול לקבוע את הקואורדינטות של המסך עבור כל תיבה תוחמת של תו גלוי
בווידג'ט TextView
. Services (שירותים)
למצוא את הקואורדינטות האלה באמצעות
refreshWithExtraData()
עובר
EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
כארגומנט הראשון ואובייקט Bundle
כארגומנט השני. בזמן שה-method מתבצעת, המערכת מאכלסת את
Bundle
ארגומנט עם מערך שניתן לחלקים של
Rect
אובייקטים. כל אובייקט Rect
שמייצג את התיבה התוחמת בתו מסוים.
ערכים סטנדרטיים של טווח חד-צדדי
חלק מהאובייקטים מסוג AccessibilityNodeInfo
משתמשים במופע של
AccessibilityNodeInfo.RangeInfo
כדי לציין שרכיב בממשק המשתמש יכול לקבל טווח ערכים. כשיוצרים
טווח באמצעות
RangeInfo.obtain()
או בעת אחזור הערכים הקיצוניים של הטווח באמצעות
getMin()
וגם
getMax()
,
חשוב לזכור שמכשירים עם Android בגרסה 8.0 (רמת API 26) ואילך מייצגים
טווחים חד-צדדיים בצורה סטנדרטית:
- לטווחים ללא מינימום,
Float.NEGATIVE_INFINITY
שמייצג את הערך המינימלי. - לטווחים שאין להם מקסימום,
Float.POSITIVE_INFINITY
שמייצג את הערך המקסימלי.
תגובה לאירועי נגישות
עכשיו כשהשירות שלכם מוגדר להרצה ולהאזנה לאירועים, אתם צריכים לכתוב קוד כדי
יודע מה לעשות כשAccessibilityEvent
מגיע. בתור התחלה, אפשר לשנות את
onAccessibilityEvent(AccessibilityEvent)
. בשיטה הזאת, משתמשים
getEventType()
כדי לקבוע את סוג האירוע
getContentDescription()
כדי לחלץ טקסט של תווית המשויך לתצוגה המפורטת שמפעילה את האירוע:
Kotlin
override fun onAccessibilityEvent(event: AccessibilityEvent) { var eventText: String = when (event.eventType) { AccessibilityEvent.TYPE_VIEW_CLICKED -> "Clicked: " AccessibilityEvent.TYPE_VIEW_FOCUSED -> "Focused: " else -> "" } eventText += event.contentDescription // Do something nifty with this text, like speak the composed string back to // the user. speakToUser(eventText) ... }
Java
@Override public void onAccessibilityEvent(AccessibilityEvent event) { final int eventType = event.getEventType(); String eventText = null; switch(eventType) { case AccessibilityEvent.TYPE_VIEW_CLICKED: eventText = "Clicked: "; break; case AccessibilityEvent.TYPE_VIEW_FOCUSED: eventText = "Focused: "; break; } eventText = eventText + event.getContentDescription(); // Do something nifty with this text, like speak the composed string back to // the user. speakToUser(eventText); ... }
מקורות מידע נוספים
מידע נוסף זמין במקורות המידע הבאים: