Benutzerdefinierte Ansichten barrierefreier gestalten

Wenn für Ihre Anwendung eine benutzerdefinierte Ansichtskomponente erforderlich ist, müssen Sie die Barrierefreiheit der Ansicht verbessern. Mit den folgenden Schritten können Sie die Barrierefreiheit Ihrer benutzerdefinierten Ansicht verbessern (siehe Beschreibung auf dieser Seite):

  • Umgang mit Klicks von Richtungscontrollern.
  • Implementieren Sie Accessibility API-Methoden.
  • Senden Sie AccessibilityEvent-Objekte, die für Ihre benutzerdefinierte Ansicht spezifisch sind.
  • Füllen Sie AccessibilityEvent und AccessibilityNodeInfo für Ihre Ansicht aus.

Klicks von Richtungscontrollern verarbeiten

Auf den meisten Geräten wird durch Klicken auf eine Ansicht mit einem Richtungscontroller ein KeyEvent mit KEYCODE_DPAD_CENTER an die aktuell fokussierte Ansicht gesendet. In allen Standard-Android-Ansichten wird KEYCODE_DPAD_CENTER entsprechend verarbeitet. Wenn Sie ein benutzerdefiniertes View-Steuerelement erstellen, achten Sie darauf, dass dieses Ereignis denselben Effekt hat wie das Tippen auf die Ansicht auf dem Touchscreen.

Das benutzerdefinierte Steuerelement muss das Ereignis KEYCODE_ENTER wie KEYCODE_DPAD_CENTER behandeln. Dies vereinfacht die Interaktion mit einer vollständigen Tastatur für die Nutzenden.

Accessibility API-Methoden implementieren

Bedienungshilfen sind Meldungen zu den Interaktionen von Nutzern mit den Komponenten der visuellen Benutzeroberfläche Ihrer App. Diese Nachrichten werden von Bedienungshilfen verarbeitet, die die Informationen in diesen Ereignissen verwenden, um zusätzliches Feedback und zusätzliche Aufforderungen zu geben. Die Bedienungshilfen sind Teil der Klassen View und View.AccessibilityDelegate. Folgende Methoden sind verfügbar:

dispatchPopulateAccessibilityEvent()
Das System ruft diese Methode auf, wenn Ihre benutzerdefinierte Ansicht ein Bedienungshilfen-Ereignis generiert. Die Standardimplementierung dieser Methode ruft onPopulateAccessibilityEvent() für diese Ansicht und dann die dispatchPopulateAccessibilityEvent()-Methode für jedes untergeordnete Element dieser Ansicht auf.
onInitializeAccessibilityEvent()
Das System ruft diese Methode auf, um über den Textinhalt hinaus zusätzliche Informationen zum Status der Ansicht zu erhalten. Wenn die benutzerdefinierte Ansicht neben einem einfachen TextView oder Button auch interaktive Steuerelemente bietet, überschreiben Sie diese Methode und legen Sie mit dieser Methode die zusätzlichen Informationen zu der Ansicht fest, z. B. Passwortfeldtyp, Kästchentyp oder Status, die Nutzerinteraktionen oder Feedback zum Ereignis ermöglichen. Wenn Sie diese Methode überschreiben, rufen Sie die zugehörige Superimplementierung auf und ändern Sie nur Attribute, die nicht von der übergeordneten Klasse festgelegt werden.
onInitializeAccessibilityNodeInfo()
Bei dieser Methode erhalten Bedienungshilfen Informationen zum Status der Ansicht. Die Standardimplementierung View verfügt über einen Standardsatz von Ansichtseigenschaften. Wenn Ihre benutzerdefinierte Ansicht jedoch interaktive Steuerungsmöglichkeiten bietet, die über eine einfache TextView oder Button hinausgehen, überschreiben Sie diese Methode und legen die zusätzlichen Informationen über die Ansicht in das AccessibilityNodeInfo-Objekt fest, das mit dieser Methode verarbeitet wird.
onPopulateAccessibilityEvent()
Mit dieser Methode wird der gesprochene Text-Prompt von AccessibilityEvent für die Ansicht festgelegt. Sie wird auch aufgerufen, wenn die Ansicht einer Ansicht untergeordnet ist, die ein Bedienungshilfen-Ereignis generiert.
onRequestSendAccessibilityEvent()
Das System ruft diese Methode auf, wenn ein der Ansicht untergeordnetes Element ein AccessibilityEvent generiert. In diesem Schritt kann der Termin zur Barrierefreiheit in der übergeordneten Ansicht um zusätzliche Informationen ergänzt werden. Implementieren Sie diese Methode nur, wenn Ihre benutzerdefinierte Ansicht untergeordnete Ansichten haben kann und die übergeordnete Ansicht Kontextinformationen für das Bedienungshilfen-Ereignis bereitstellen kann, die für Bedienungshilfen nützlich sind.
sendAccessibilityEvent()
Das System ruft diese Methode auf, wenn ein Nutzer eine Aktion für eine Ansicht ausführt. Das Ereignis wird mit einem Nutzeraktionstyp wie TYPE_VIEW_CLICKED klassifiziert. Im Allgemeinen müssen Sie immer dann ein AccessibilityEvent senden, wenn sich der Inhalt der benutzerdefinierten Ansicht ändert.
sendAccessibilityEventUnchecked()
Diese Methode wird verwendet, wenn der aufrufende Code die Prüfung der Barrierefreiheit auf dem Gerät direkt steuern muss (AccessibilityManager.isEnabled()). Wenn Sie diese Methode implementieren, führen Sie den Aufruf unabhängig von der Systemeinstellung so aus, als ob die Bedienungshilfen aktiviert sind. In der Regel müssen Sie diese Methode für eine benutzerdefinierte Ansicht nicht implementieren.

Um die Barrierefreiheit zu unterstützen, überschreiben und implementieren Sie die vorherigen Bedienungshilfen direkt in Ihrer benutzerdefinierten Ansichtsklasse.

Implementieren Sie mindestens die folgenden Bedienungshilfen für Ihre benutzerdefinierte Ansichtsklasse:

  • dispatchPopulateAccessibilityEvent()
  • onInitializeAccessibilityEvent()
  • onInitializeAccessibilityNodeInfo()
  • onPopulateAccessibilityEvent()

Weitere Informationen zum Implementieren dieser Methoden finden Sie im Abschnitt zum Ausfüllen von Bedienungshilfen-Ereignissen.

Bedienungshilfen-Ereignisse senden

Abhängig von den Besonderheiten Ihrer benutzerdefinierten Ansicht müssen möglicherweise zu verschiedenen Zeiten oder für Ereignisse, die nicht von der Standardimplementierung verarbeitet werden, AccessibilityEvent-Objekte gesendet werden. Die Klasse View bietet eine Standardimplementierung für diese Ereignistypen:

Im Allgemeinen müssen Sie immer dann eine AccessibilityEvent senden, wenn sich der Inhalt der benutzerdefinierten Ansicht ändert. Wenn Sie beispielsweise eine benutzerdefinierte Schiebereglerleiste implementieren, mit der der Nutzer durch Drücken der Links- oder Rechtspfeiltaste einen numerischen Wert auswählen kann, muss Ihre benutzerdefinierte Ansicht das Ereignis TYPE_VIEW_TEXT_CHANGED ausgeben, wenn sich der Wert des Schiebereglers ändert. Im folgenden Codebeispiel wird die Verwendung der Methode sendAccessibilityEvent() zum Melden dieses Ereignisses veranschaulicht.

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;
    }
    ...
}

Ereignisse zur Barrierefreiheit ausfüllen

Jedes AccessibilityEvent hat eine Reihe von erforderlichen Attributen, die den aktuellen Status der Ansicht beschreiben. Zu diesen Attributen gehören beispielsweise der Klassenname der Ansicht, die Inhaltsbeschreibung und der Status „Prüfung“. Die spezifischen Attribute, die für jeden Ereignistyp erforderlich sind, sind in der Referenzdokumentation zu AccessibilityEvent beschrieben.

Die View-Implementierung stellt Standardwerte für diese erforderlichen Attribute bereit. Viele dieser Werte, einschließlich des Klassennamens und des Ereigniszeitstempels, werden automatisch bereitgestellt. Wenn Sie eine benutzerdefinierte Ansichtskomponente erstellen, müssen Sie Informationen zum Inhalt und zu den Eigenschaften der Ansicht angeben. Diese Informationen können einfach ein Schaltflächenlabel sein und zusätzliche Statusinformationen enthalten, die Sie dem Ereignis hinzufügen möchten.

Mit den Methoden onPopulateAccessibilityEvent() und onInitializeAccessibilityEvent() können Sie die Informationen in einem AccessibilityEvent eintragen oder ändern. Verwenden Sie die Methode onPopulateAccessibilityEvent() speziell zum Hinzufügen oder Ändern des Textinhalts des Ereignisses, der von Bedienungshilfen wie TalkBack in hörbare Aufforderungen umgewandelt wird. Verwenden Sie die Methode onInitializeAccessibilityEvent(), um zusätzliche Informationen zum Ereignis einzugeben, z. B. den Auswahlstatus der Ansicht.

Implementieren Sie außerdem die Methode onInitializeAccessibilityNodeInfo(). Bedienungshilfen verwenden die AccessibilityNodeInfo-Objekte, die mit dieser Methode ausgefüllt werden, um die Ansichtshierarchie zu untersuchen, die ein Bedienungshilfen-Ereignis nach Empfang generiert, und um Nutzern angemessenes Feedback zu geben.

Das folgende Codebeispiel zeigt, wie diese drei Methoden in der Ansicht überschrieben werden können:

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);
    }
}

Sie können diese Methoden direkt in Ihrer benutzerdefinierten Ansichtsklasse implementieren.

Angepassten Kontext zur Barrierefreiheit bereitstellen

Bedienungshilfen können die Hierarchie der zugehörigen Ansicht einer Komponente der Benutzeroberfläche prüfen, die ein Bedienungshilfen-Ereignis generiert. Auf diese Weise können Bedienungshilfen umfassendere Kontextinformationen zur Unterstützung der Nutzer bereitstellen.

Es gibt Fälle, in denen Bedienungshilfen keine angemessenen Informationen aus der Ansichtshierarchie abrufen können. Ein Beispiel hierfür ist ein benutzerdefiniertes UI-Steuerelement mit zwei oder mehr separat anklickbaren Bereichen, z. B. einem Kalendersteuerelement. In diesem Fall können die Dienste keine angemessenen Informationen abrufen, da die anklickbaren Unterabschnitte nicht Teil der Ansichtshierarchie sind.

Abbildung 1: Eine benutzerdefinierte Kalenderansicht mit auswählbaren Tageselementen

Im Beispiel in Abbildung 1 ist der gesamte Kalender als einzelne Ansicht implementiert, sodass Bedienungshilfen nur dann genügend Informationen über den Inhalt der Ansicht und die Auswahl des Nutzers in der Ansicht erhalten, wenn der Entwickler zusätzliche Informationen bereitstellt. Wenn ein Nutzer beispielsweise auf den Tag mit dem Label 17 klickt, erhält das Framework für Barrierefreiheit nur die Beschreibungsinformationen für das gesamte Kalendersteuerelement. In diesem Fall kündigt die TalkBack-Bedienungshilfe „Kalender“ oder „Kalender für April“ an und der Nutzer weiß nicht, welcher Tag ausgewählt ist.

Um in solchen Situationen geeignete Kontextinformationen für Bedienungshilfen bereitzustellen, ermöglicht das Framework die Festlegung einer virtuellen Ansichtshierarchie. Mit einer virtuellen Ansichtshierarchie können App-Entwickler eine ergänzende Ansichtshierarchie zu den Bedienungshilfen bereitstellen, die den Informationen auf dem Bildschirm besser entspricht. Auf diese Weise können Bedienungshilfen nützliche Kontextinformationen für Nutzer bereitstellen.

Eine weitere Situation, in der eine Hierarchie für eine virtuelle Ansicht erforderlich sein kann, ist eine Benutzeroberfläche mit einer Reihe von View-Steuerelementen mit eng verbundenen Funktionen. Eine Aktion für ein Steuerelement wirkt sich auf den Inhalt eines oder mehrerer Elemente aus, z. B. eine Zahlenauswahl mit separaten Aufwärts- und Abwärtspfeilen. In diesem Fall können die Bedienungshilfen keine angemessenen Informationen abrufen, da durch eine Aktion an einem Steuerelement Inhalte in einem anderen geändert werden und die Beziehung dieser Steuerelemente für den Dienst möglicherweise nicht offensichtlich ist.

Gruppieren Sie in diesem Fall die zugehörigen Steuerelemente mit einer beinhaltenden Ansicht und stellen Sie eine virtuelle Ansichtshierarchie aus diesem Container bereit, um die Informationen und das Verhalten der Steuerelemente klar darzustellen.

Wenn Sie eine virtuelle Ansichtshierarchie für eine Ansicht bereitstellen möchten, überschreiben Sie die Methode getAccessibilityNodeProvider() in der benutzerdefinierten Ansicht oder Ansichtsgruppe und geben Sie eine Implementierung von AccessibilityNodeProvider zurück. Sie können eine Hierarchie für virtuelle Ansichten implementieren, indem Sie die Supportbibliothek mit der Methode ViewCompat.getAccessibilityNodeProvider() verwenden und eine Implementierung mit AccessibilityNodeProviderCompat bereitstellen.

Sie können stattdessen ExploreByTouchHelper implementieren, um die Bereitstellung von Informationen für Bedienungshilfen und die Verwaltung des Barrierefreiheitsfokus zu vereinfachen. Sie stellt ein AccessibilityNodeProviderCompat bereit und kann durch Aufrufen von setAccessibilityDelegate als AccessibilityDelegateCompat einer Ansicht angehängt werden. Ein Beispiel finden Sie unter ExploreByTouchHelperActivity. ExploreByTouchHelper wird auch von Framework-Widgets wie CalendarView über die untergeordnete Ansicht SimpleMonthView verwendet.

Benutzerdefinierte Touch-Ereignisse verarbeiten

Für die Steuerelemente der benutzerdefinierten Ansicht ist möglicherweise ein nicht standardmäßiges Verhalten bei Touch-Ereignissen erforderlich, wie in den folgenden Beispielen veranschaulicht.

Klickbasierte Aktionen definieren

Wenn Ihr Widget die Schnittstelle OnClickListener oder OnLongClickListener verwendet, verarbeitet das System die Aktionen ACTION_CLICK und ACTION_LONG_CLICK für Sie. Wenn Ihre App ein stärker angepasstes Widget verwendet, das auf der Schnittstelle OnTouchListener basiert, definieren Sie benutzerdefinierte Handler für die klickbasierten Bedienungshilfen. Rufen Sie dazu für jede Aktion die Methode replaceAccessibilityAction() auf, wie im folgenden Code-Snippet gezeigt:

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());
}

Benutzerdefinierte Klickereignisse erstellen

Ein benutzerdefiniertes Steuerelement kann mit der Listener-Methode onTouchEvent(MotionEvent) die Ereignisse ACTION_DOWN und ACTION_UP erkennen und ein spezielles Klickereignis auslösen. Um die Kompatibilität mit Bedienungshilfen zu gewährleisten, muss der Code, der dieses benutzerdefinierte Klickereignis verarbeitet, Folgendes tun:

  1. Generieren Sie ein entsprechendes AccessibilityEvent-Objekt für die interpretierte Klickaktion.
  2. Aktivieren Sie Bedienungshilfen, damit diese die benutzerdefinierte Klickaktion für Nutzer ausführen können, die keinen Touchscreen verwenden können.

Um diese Anforderungen effizient zu erfüllen, muss Ihr Code die Methode performClick() überschreiben, die die Superimplementierung dieser Methode aufrufen und dann alle für das Klickereignis erforderlichen Aktionen ausführen muss. Wenn die benutzerdefinierte Klickaktion erkannt wird, muss dieser Code dann die Methode performClick() aufrufen. Das folgende Codebeispiel veranschaulicht dieses Muster.

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;
    }
}

Mit dem vorherigen Muster wird sichergestellt, dass das benutzerdefinierte Klickereignis mit Bedienungshilfen kompatibel ist. Dazu wird mit der Methode performClick() ein Bedienungshilfenereignis generiert und ein Einstiegspunkt für Bedienungshilfen bereitgestellt, damit diese im Namen eines Nutzers handeln können, der das benutzerdefinierte Klickereignis ausführt.