Eine Bedienungshilfe ist eine App, die die Benutzeroberfläche verbessert, um Nutzern mit Behinderungen oder Nutzern, die vorübergehend nicht in der Lage sind, vollständig mit einem Gerät zu interagieren, zu helfen. Diese Dienste werden im Hintergrund ausgeführt und kommunizieren mit dem System, um Bildschirminhalte zu prüfen und im Namen des Nutzers mit Apps zu interagieren. Beispiele sind Screenreader (z. B. TalkBack), Tools für den Schalterzugriff und Sprachsteuerungssysteme.
In diesem Leitfaden werden die Grundlagen zum Erstellen einer Android-Bedienungshilfe behandelt.
Lebenszyklus von Bedienungshilfen
Wenn Sie eine Bedienungshilfe erstellen möchten, müssen Sie die Klasse AccessibilityService erweitern und den Dienst im Manifest Ihrer App deklarieren.
Dienstklasse erstellen
Erstellen Sie eine Klasse, die AccessibilityService erweitert. Sie müssen die folgenden Methoden überschreiben:
onAccessibilityEvent: Wird aufgerufen, wenn das System ein Ereignis erkennt, das der Konfiguration Ihres Dienstes entspricht (z. B. Fokusänderung oder Schaltflächenklick). Hier interpretiert Ihr Dienst die Benutzeroberfläche.onInterrupt: Wird aufgerufen, wenn das System das Feedback Ihres Dienstes unterbricht, z. B. um die Sprachausgabe zu beenden, wenn der Nutzer den Fokus schnell verschiebt.
package com.example.android.apis.accessibility import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.FingerprintGestureController import android.accessibilityservice.AccessibilityButtonController import android.accessibilityservice.GestureDescription import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import android.graphics.Path import android.os.Build import android.media.AudioManager import android.content.Context class MyAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { // Interpret the event and provide feedback to the user } override fun onInterrupt() { // Interrupt any ongoing feedback } override fun onServiceConnected() { // Perform initialization here } }
Im Manifest deklarieren
Registrieren Sie Ihren Dienst in der Datei AndroidManifest.xml. Sie müssen die Berechtigung BIND_ACCESSIBILITY_SERVICE strikt erzwingen, damit nur das System eine Bindung an Ihren Dienst vornehmen kann.
Damit die Schaltfläche „Einstellungen“ funktioniert, müssen Sie die ServiceSettingsActivity deklarieren.
<application> <service android:name=".accessibility.MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:exported="true" android:label="@string/accessibility_service_label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> <activity android:name=".accessibility.ServiceSettingsActivity" android:exported="true" android:label="@string/accessibility_service_settings_label" /> </application>
Dienst konfigurieren
Erstellen Sie eine Konfigurationsdatei in res/xml/accessibility_service_config.xml. In dieser Datei wird definiert, welche Ereignisse von Ihrem Dienst verarbeitet werden und welches Feedback er gibt.
Achten Sie darauf, dass Sie auf den ServiceSettingsActivity verweisen, den Sie in Ihrem Manifest deklariert haben:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_description" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagDefault|flagRequestFingerprintGestures|flagRequestAccessibilityButton" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:canRetrieveWindowContent="true" android:canPerformGestures="true" android:settingsActivity="com.example.android.apis.accessibility.ServiceSettingsActivity" />
Die Konfigurationsdatei enthält die folgenden wichtigen Attribute:
android:accessibilityEventTypes: Die Ereignisse, die Sie empfangen möchten. Verwenden SietypeAllMaskfür einen Dienst für allgemeine Zwecke.android:canRetrieveWindowContent: Musstruesein, wenn Ihr Dienst die UI-Hierarchie prüfen muss (z. B. um Text vom Bildschirm zu lesen).android:canPerformGestures: Musstruesein, wenn Sie Gesten (z. B. Wisch- oder Tippvorgänge) programmatisch senden möchten.android:accessibilityFlags: Flags kombinieren, um Funktionen zu aktivieren.flagRequestFingerprintGesturesist für Fingerabdruckgesten erforderlich.flagRequestAccessibilityButtonist für die Schaltfläche „Bedienungshilfen“ erforderlich.
Eine vollständige Liste der Konfigurationsoptionen finden Sie unter AccessibilityServiceInfo.
Laufzeitkonfiguration
Die XML-Konfiguration ist statisch. Sie können die Dienstkonfiguration aber auch dynamisch zur Laufzeit ändern. Das ist nützlich, um Funktionen basierend auf den Nutzereinstellungen zu aktivieren oder zu deaktivieren.
Überschreiben Sie onServiceConnected(), um Laufzeitupdates mit setServiceInfo() anzuwenden:
override fun onServiceConnected() { val info = AccessibilityServiceInfo() // Set the type of events that this service wants to listen to. info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED // Set the type of feedback your service provides. info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN // Set flags at runtime. info.flags = AccessibilityServiceInfo.FLAG_DEFAULT or AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES this.setServiceInfo(info) }
Inhalte der Benutzeroberfläche interpretieren
Wenn onAccessibilityEvent() ausgelöst wird, stellt das System eine AccessibilityEvent bereit. Dieses Ereignis dient als Einstiegspunkt für den Baum für Barrierefreiheit, eine hierarchische Darstellung des Bildschirminhalts.
Ihr Dienst interagiert hauptsächlich mit AccessibilityNodeInfo-Objekten, die UI-Elemente wie Schaltflächen, Listen und Text darstellen. Daten zu diesen UI-Elementen werden in AccessibilityNodeInfo normalisiert.
Das folgende Beispiel zeigt, wie Sie die Quelle eines Ereignisses abrufen und den Barrierefreiheitsbaum durchlaufen, um Informationen zu finden.
override fun onAccessibilityEvent(event: AccessibilityEvent) { // Get the source node of the event val sourceNode: AccessibilityNodeInfo? = event.source if (sourceNode == null) return // Inspect properties if (sourceNode.isCheckable) { val state = if (sourceNode.isChecked) "Checked" else "Unchecked" val label = sourceNode.text ?: sourceNode.contentDescription // Provide feedback (for example, speak to the user) speakToUser("$label is $state") } // Always recycle nodes to prevent memory leaks sourceNode.recycle() } private fun speakToUser(text: String) { // Your text-to-speech implementation goes here }
Im Namen von Nutzern handeln
Bedienungshilfen können Aktionen wie das Klicken auf Schaltflächen oder das Scrollen von Listen im Namen des Nutzers ausführen.
Rufen Sie performAction() für ein AccessibilityNodeInfo-Objekt auf, um eine Aktion auszuführen.
fun performClick(node: AccessibilityNodeInfo) { if (node.isClickable) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } }
Verwenden Sie performGlobalAction() für globale Aktionen, die das gesamte System betreffen, z. B. das Drücken des Buttons „Zurück“ oder das Öffnen der Benachrichtigungsleiste.
// Navigate back fun navigateBack() { performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }
Fokus verwalten
Android hat zwei verschiedene Arten von Fokus: Eingabefokus (wo Tastatureingaben eingehen) und Bedienungshilfen-Fokus (was der Bedienungshilfen-Dienst prüft).
Das folgende Snippet zeigt, wie Sie das Element finden, das derzeit den Fokus für die Barrierefreiheit hat:
// Find the node that currently has accessibility focus // Note: rootInActiveWindow can be null if the window is not available val root = rootInActiveWindow if (root != null) { val focusedNode = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) // Do something with focusedNode // Always recycle nodes focusedNode?.recycle() // rootInActiveWindow doesn't need to be recycled, but obtained nodes do. }
Das folgende Snippet zeigt, wie der Fokus der Barrierefreiheit auf ein bestimmtes Element verschoben wird:
// Request that the system give focus to a given node fun focusNode(node: AccessibilityNodeInfo) { node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) }
Achten Sie beim Erstellen eines Bedienungshilfendienstes auf den Fokusstatus des Nutzers und vermeiden Sie es, den Fokus zu übernehmen, es sei denn, dies wird explizit durch eine Nutzeraktion ausgelöst.
Touch-Gesten möglich
Ihr Dienst kann benutzerdefinierte Touch-Gesten auf dem Bildschirm ausführen, z. B. Wischen, Tippen oder Multitouch-Interaktionen. Dazu müssen Sie android:canPerformGestures="true" in Ihrer Konfiguration deklarieren, damit Sie die dispatchGesture() API verwenden können.
Einfache Touch-Gesten
Um einfache Gesten auszuführen, erstellen Sie zuerst ein Path-Objekt, das die Bewegung einer bestimmten Geste darstellt. Umschließe dann das Path mit einem GestureDescription, um den Strich zu beschreiben. Rufen Sie schließlich dispatchGesture auf, um die Geste zu senden.
fun swipeRight() { // Create a path for the swipe (from x=100 to x=500) val swipePath = Path() swipePath.moveTo(100f, 500f) swipePath.lineTo(500f, 500f) // Build the stroke description (0ms delay, 500ms duration) val stroke = GestureDescription.StrokeDescription(swipePath, 0, 500) // Build the gesture description val gestureBuilder = GestureDescription.Builder() gestureBuilder.addStroke(stroke) // Dispatch the gesture dispatchGesture(gestureBuilder.build(), object : AccessibilityService.GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription?) { super.onCompleted(gestureDescription) // Gesture finished successfully } }, null) }
Fortgesetzte Gesten
Bei komplexen Interaktionen wie dem Zeichnen einer L-Form oder einem präzisen mehrstufigen Ziehen können Sie Striche mit dem Parameter willContinue verketten.
fun performLShapedGesture() { val path1 = Path().apply { moveTo(200f, 200f) lineTo(400f, 200f) } val path2 = Path().apply { moveTo(400f, 200f) lineTo(400f, 400f) } // First stroke: willContinue = true val stroke1 = GestureDescription.StrokeDescription(path1, 0, 500, true) // Second stroke: continues immediately after stroke1 val stroke2 = stroke1.continueStroke(path2, 0, 500, false) val builder = GestureDescription.Builder() builder.addStroke(stroke1) builder.addStroke(stroke2) dispatchGesture(builder.build(), null, null) }
Audioverwaltung
Verwenden Sie beim Erstellen eines Bedienungshilfendienstes (insbesondere eines Screenreaders) den STREAM_ACCESSIBILITY-Audiostream. So können Nutzer die Lautstärke des Dienstes unabhängig von der Lautstärke der Systemmedien steuern.
fun increaseAccessibilityVolume() { val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager.adjustStreamVolume( AudioManager.STREAM_ACCESSIBILITY, AudioManager.ADJUST_RAISE, 0 ) }
Achten Sie darauf, dass Sie das Flag FLAG_ENABLE_ACCESSIBILITY_VOLUME in Ihre Konfiguration aufnehmen, entweder in XML oder über setServiceInfo zur Laufzeit.
Erweiterte Funktionen
Touch-Gesten auf dem Fingerabdrucksensor
Auf Geräten mit Android 10 (API-Level 29) oder höher kann Ihr Dienst Richtungs-Wischbewegungen auf dem Fingerabdrucksensor erfassen. Das ist nützlich, um alternative Navigationssteuerelemente bereitzustellen.
Fügen Sie Ihrer onServiceConnected()-Methode die folgende Logik hinzu:
// Import: android.os.Build // Import: android.accessibilityservice.FingerprintGestureController private var gestureController: FingerprintGestureController? = null override fun onServiceConnected() { // Check if the device is running Android 10 (Q) or higher if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { gestureController = fingerprintGestureController val callback = object : FingerprintGestureController.FingerprintGestureCallback() { override fun onGestureDetected(gesture: Int) { when (gesture) { FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN -> { // Handle swipe down } FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP -> { // Handle swipe up } } } } gestureController?.registerFingerprintGestureCallback(callback, null) } }
Schaltfläche "Bedienungshilfen"
Auf Geräten mit Software-Navigationsschaltflächen können Nutzer Ihren Dienst über den Button „Bedienungshilfen“ in der Navigationsleiste aufrufen.
Wenn Sie diese Funktion verwenden möchten, fügen Sie Ihrer Dienstkonfiguration das Flag FLAG_REQUEST_ACCESSIBILITY_BUTTON hinzu. Fügen Sie dann die Registrierungslogik in Ihre onServiceConnected()-Methode ein.
// Import: android.accessibilityservice.AccessibilityButtonController override fun onServiceConnected() { // ... existing initialization code ... val controller = accessibilityButtonController controller.registerAccessibilityButtonCallback( object : AccessibilityButtonController.AccessibilityButtonCallback() { override fun onClicked(controller: AccessibilityButtonController) { // Respond to button tap } } ) }
Mehrsprachige Sprachausgabe
Ein Dienst, der Text vorliest, kann automatisch die Sprache wechseln, wenn der Quelltext mit LocaleSpan gekennzeichnet ist. So kann Ihr Dienst Inhalte in verschiedenen Sprachen korrekt aussprechen, ohne dass Sie manuell umschalten müssen.
import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.LocaleSpan import java.util.Locale // Wrap text in LocaleSpan to indicate language val spannable = SpannableStringBuilder("Bonjour") spannable.setSpan( LocaleSpan(Locale.FRANCE), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE )
Wenn Ihr Dienst AccessibilityNodeInfo verarbeitet, prüfen Sie die Eigenschaft text für LocaleSpan-Objekte, um die richtige Sprache für die Sprachsynthese zu ermitteln.
Zusätzliche Ressourcen
Weitere Informationen finden Sie in den folgenden Ressourcen:
Leitfäden
- Barrierefreie Apps entwickeln
- Barrierefreiheit in Jetpack Compose
- Kurzanleitung: Barrierefreiheit in Compose