it-swarm.com.de

So erstellen Sie in ConstraintLayout barrierefreie Fokusgruppen?

Stellen Sie sich vor, Sie haben eine LinearLayout in einer RelativeLayout, die 3 TextViews mit artist, song and album enthält:

<RelativeLayout
    ...
    <LinearLayout
        Android:id="@id/text_view_container"
        Android:layout_width="warp_content"
        Android:layout_height="wrap_content"
        Android:orientation="vertical">

        <TextView
            Android:id="@id/artist"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Artist"/>

        <TextView
            Android:id="@id/song"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Song"/>

        <TextView
            Android:id="@id/album"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="album"/>
    </LinearLayout>

    <TextView
        Android:id="@id/unrelated_textview1/>
    <TextView
        Android:id="@id/unrelated_textview2/>
    ...
</RelativeLayout>        

Wenn Sie den TalkbackReader aktivieren und in der TextView auf eine LinearLayout klicken, liest der TalkbackReader beispielsweise "Artist", "Song" OR "Album".

Sie können die ersten 3 TextViews jedoch in eine Fokusgruppe einfügen, indem Sie Folgendes verwenden:

<LinearLayout
    Android:focusable="true
    ...

Jetzt würde der TalkbackReader "Artist Song Album" lesen. 

Die 2 unrelated TextViewsstill wäre für sich alleine und nicht gelesen. Das ist das Verhalten, das ich erreichen möchte.

(Siehe Beispiel für Google Codelabs als Referenz)

Ich versuche jetzt, dieses Verhalten mit ConstrainLayout neu zu erstellen, sehe aber nicht wie.

<ConstraintLayout>
    <TextView artist/>
    <TextView song/>
    <TextView album/>
    <TextView unrelated_textview1/>
    <TextView unrelated_textview2/>
</ConstraintLayout>

Das Einfügen von Widgets in eine "Gruppe" scheint nicht zu funktionieren:

<Android.support.constraint.Group
    Android:id="@+id/group"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:focusable="true"
    Android:importantForAccessibility="yes"
    app:constraint_referenced_ids="artist,song,album"
    />

Wie kann ich Fokusgruppen für die Erreichbarkeit in ConstrainLayout neu erstellen?

[EDIT]: Es scheint so zu sein, dass die einzige Möglichkeit zum Erstellen einer Lösung die Verwendung von "focusable = true" für das äußere ConstraintLayout und/oder "focusable = false" für die Ansichten selbst ist . Dies hat einige Nachteile, die man beim Umgang mit Tastaturnavigation/Schaltboxen beachten sollte: 

https://github.com/googlecodelabs/Android-accessibility/issues/4

18
hamena314

Die auf ViewGroups basierenden Fokusgruppen funktionieren weiterhin in ConstraintLayout, sodass Sie LinearLayouts und RelativeLayouts durch ConstraintLayouts ersetzen können und TalkBack weiterhin wie erwartet funktioniert. Wenn Sie jedoch versuchen, VerschachtelungViewGroups innerhalb von ConstraintLayout zu vermeiden und das Konstruktionsziel einer flachen Ansichtshierarchie einzuhalten, können Sie dies tun.

Bewegen Sie den TextViews aus dem Fokus ViewGroup, den Sie direkt ansprechen, in den obersten Bereich ConstraintLayout. Jetzt werden wir ein einfaches transparentes View über diese TextViews mit ConstraintLayout-Einschränkungen setzen. Jeder TextView ist ein Mitglied der obersten Ebene ConstraintLayout, daher ist das Layout flach. Da sich das Overlay auf dem TextViews befindet, werden alle Berührungsereignisse vor dem zugrunde liegenden TextViews empfangen. Hier ist die Layoutstruktur:

<ConstaintLayout>
    <TextView>
    <TextView>
    <TextView>
    <View> [overlays the above TextViews]
</ConstraintLayout>

Wir können jetzt manuell eine Inhaltsbeschreibung für die Überlagerung angeben, die eine Kombination aus dem Text jedes zugrunde liegenden TextViews ist. Um zu verhindern, dass jeder TextView den Fokus akzeptiert und seinen eigenen Text spricht, setzen wir Android:importantForAccessibility="no". Wenn wir die Überlagerungsansicht berühren, hören wir den kombinierten Text der gesprochenen TextViews.

Das Vorhergehende ist die allgemeine Lösung, aber noch besser wäre eine Implementierung einer benutzerdefinierten Overlay-Ansicht, die die Dinge automatisch verwaltet. Die unten abgebildete benutzerdefinierte Überlagerung folgt der allgemeinen Syntax des Group-Helpers in ConstraintLayout und automatisiert einen Großteil der oben beschriebenen Verarbeitung.

Die benutzerdefinierte Überlagerung führt Folgendes aus:

  1. Akzeptiert eine Liste von IDs, die vom Steuerelement wie der Group-Helfer von ConstraintLayout gruppiert werden. 
  2. Deaktiviert den Zugriff für die gruppierten Steuerelemente durch Festlegen von View.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO) in jeder Ansicht. (Dies vermeidet, dass dies manuell durchgeführt werden muss.)
  3. Wenn Sie darauf klicken, zeigt das benutzerdefinierte Steuerelement eine Verkettung des Texts von gruppierten Ansichten an das Eingabehilfsrahmen an. Der für eine Ansicht gesammelte Text stammt entweder aus contentDescription, getText() oder hint. (Dies vermeidet, dass dies manuell durchgeführt werden muss. Ein weiterer Vorteil ist, dass auch Änderungen am Text übernommen werden, während die App ausgeführt wird.)

Die Überlagerungsansicht muss noch manuell innerhalb der Layout-XML positioniert werden, um TextViews zu überlagern.

Hier ist ein Beispiel-Layout, das den in der Frage erwähnten ViewGroup-Ansatz und die benutzerdefinierte Überlagerung zeigt. Die linke Gruppe ist der traditionelle ViewGroup-Ansatz, der die Verwendung eines eingebetteten ConstraintLayout zeigt. Das Recht ist die Überlagerungsmethode, die das benutzerdefinierte Steuerelement verwendet. Das TextView oben mit der Bezeichnung "Anfangsfokus" dient nur dazu, den anfänglichen Fokus für den Vergleich der beiden Methoden zu erfassen.

Wenn ConstraintLayout ausgewählt ist, spricht TalkBack "Interpret, Song, Album".

 enter image description here

Wenn die benutzerdefinierte Ansichtsüberlagerung ausgewählt ist, spricht TalkBack auch "Interpret, Song, Album". 

 enter image description here

Nachfolgend finden Sie das Beispiellayout und den Code für die benutzerdefinierte Ansicht. Achtung: Diese benutzerdefinierte Ansicht funktioniert zwar mit dem angegebenen Zweck mit TextViews, ist jedoch kein zuverlässiger Ersatz für die herkömmliche Methode. Beispiel: Das benutzerdefinierte Overlay spricht den Text von Ansichtstypen, die TextView wie EditText erweitern, während die herkömmliche Methode verwendet wird nicht.

Siehe das Beispielprojekt auf GitHub.

activity_main.xml

<Android.support.constraint.ConstraintLayout 
    Android:id="@+id/layout"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.constraint.ConstraintLayout
        Android:id="@+id/viewGroup"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:focusable="true"
        Android:gravity="center_horizontal"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/viewGroupHeading">

        <TextView
            Android:id="@+id/artistText"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Artist"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            Android:id="@+id/songText"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="16dp"
            Android:text="Song"
            app:layout_constraintStart_toStartOf="@+id/artistText"
            app:layout_constraintTop_toBottomOf="@+id/artistText" />

        <TextView
            Android:id="@+id/albumText"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="16dp"
            Android:text="Album"
            app:layout_constraintStart_toStartOf="@+id/songText"
            app:layout_constraintTop_toBottomOf="@+id/songText" />

    </Android.support.constraint.ConstraintLayout>

    <TextView
        Android:id="@+id/artistText2"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Artist"
        app:layout_constraintBottom_toTopOf="@+id/songText2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="@+id/viewGroup" />

    <TextView
        Android:id="@+id/songText2"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="16dp"
        Android:text="Song"
        app:layout_constraintStart_toStartOf="@id/artistText2"
        app:layout_constraintTop_toBottomOf="@+id/artistText2" />

    <TextView
        Android:id="@+id/albumText2"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="16dp"
        Android:text="Album"
        app:layout_constraintStart_toStartOf="@+id/artistText2"
        app:layout_constraintTop_toBottomOf="@+id/songText2" />

    <com.example.constraintlayoutaccessibility.AccessibilityOverlay
        Android:id="@+id/overlay"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:focusable="true"
        app:accessible_group="artistText2, songText2, albumText2, editText2, button2"
        app:layout_constraintBottom_toBottomOf="@+id/albumText2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@id/viewGroup" />

    <Android.support.constraint.Guideline
        Android:id="@+id/guideline"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />

    <TextView
        Android:id="@+id/viewGroupHeading"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="16dp"
        Android:importantForAccessibility="no"
        Android:text="ViewGroup"
        Android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        Android:textStyle="bold"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView4" />

    <TextView
        Android:id="@+id/overlayHeading"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:importantForAccessibility="no"
        Android:text="Overlay"
        Android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        Android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="@+id/viewGroupHeading" />

    <TextView
        Android:id="@+id/textView4"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginStart="8dp"
        Android:layout_marginTop="8dp"
        Android:layout_marginEnd="8dp"
        Android:text="Initial focus"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="parent" />

</Android.support.constraint.ConstraintLayout>

AccessibilityOverlay.Java 

public class AccessibilityOverlay extends View {
    private int[] mAccessibleIds;

    public AccessibilityOverlay(Context context) {
        super(context);
        init(context, null, 0, 0);
    }

    public AccessibilityOverlay(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0, 0);
    }

    public AccessibilityOverlay(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }

    @TargetApi(Build.VERSION_CODES.Lollipop)
    public AccessibilityOverlay(Context context, @Nullable AttributeSet attrs,
                                int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }

    private void init(Context context, @Nullable AttributeSet attrs,
                      int defStyleAttr, int defStyleRes) {
        String accessibleIdString;

        TypedArray a = context.getTheme().obtainStyledAttributes(
            attrs,
            R.styleable.AccessibilityOverlay,
            defStyleAttr, defStyleRes);

        try {
            accessibleIdString = a.getString(R.styleable.AccessibilityOverlay_accessible_group);
        } finally {
            a.recycle();
        }
        mAccessibleIds = extractAccessibleIds(context, accessibleIdString);
    }

    @NonNull
    private int[] extractAccessibleIds(@NonNull Context context, @Nullable String idNameString) {
        if (TextUtils.isEmpty(idNameString)) {
            return new int[]{};
        }
        String[] idNames = idNameString.split(ID_DELIM);
        int[] resIds = new int[idNames.length];
        Resources resources = context.getResources();
        String packageName = context.getPackageName();
        int idCount = 0;
        for (String idName : idNames) {
            idName = idName.trim();
            if (idName.length() > 0) {
                int resId = resources.getIdentifier(idName, ID_DEFTYPE, packageName);
                if (resId != 0) {
                    resIds[idCount++] = resId;
                }
            }
        }
        return resIds;
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();

        View view;
        ViewGroup parent = (ViewGroup) getParent();
        for (int id : mAccessibleIds) {
            if (id == 0) {
                break;
            }
            view = parent.findViewById(id);
            if (view != null) {
                view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
            }
        }
    }

    @Override
    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
        super.onPopulateAccessibilityEvent(event);

        int eventType = event.getEventType();
        if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED ||
            eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED &&
                getContentDescription() == null) {
            event.getText().add(getAccessibilityText());
        }
    }

    @NonNull
    private String getAccessibilityText() {
        ViewGroup parent = (ViewGroup) getParent();
        View view;
        StringBuilder sb = new StringBuilder();

        for (int id : mAccessibleIds) {
            if (id == 0) {
                break;
            }
            view = parent.findViewById(id);
            if (view != null && view.getVisibility() == View.VISIBLE) {
                CharSequence description = view.getContentDescription();

                // This misbehaves if the view is an EditText or Button or otherwise derived
                // from TextView by voicing the content when the ViewGroup approach remains
                // silent.
                if (TextUtils.isEmpty(description) && view instanceof TextView) {
                    TextView tv = (TextView) view;
                    description = tv.getText();
                    if (TextUtils.isEmpty(description)) {
                        description = tv.getHint();
                    }
                }
                if (description != null) {
                    sb.append(",");
                    sb.append(description);
                }
            }
        }
        return (sb.length() > 0) ? sb.deleteCharAt(0).toString() : "";
    }

    private static final String ID_DELIM = ",";
    private static final String ID_DEFTYPE = "id";
}

attrs.xml
Definieren Sie die benutzerdefinierten Attribute für die benutzerdefinierte Überlagerungsansicht.

<resources>  
    <declare-styleable name="AccessibilityOverlay">  
        <attr name="accessible_group" format="string" />  
    </declare-styleable>  
</resources>
1
Cheticamp

Ich bin kürzlich auf dasselbe Problem gestoßen und habe beschlossen, eine neue Klasse mithilfe der neuen ConstraintLayout-Helfer (verfügbar seit ConstraintLayout 1.1) zu implementieren, damit wir sie genauso verwenden können wie die Gruppenansicht.

Die Implementierung ist eine vereinfachte Version von Cheticamps Antwort und seiner Idee, eine neue Ansicht zu erstellen, die die Barrierefreiheit regelt.

Hier ist meine Implementierung:

package com.julienarzul.Android.accessibility

import Android.content.Context
import Android.os.Build
import Android.util.AttributeSet
import Android.view.View
import Android.view.accessibility.AccessibilityEvent
import androidx.constraintlayout.widget.ConstraintHelper
import androidx.constraintlayout.widget.ConstraintLayout

class ConstraintLayoutAccessibilityHelper
@JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {

    init {
        importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            isScreenReaderFocusable = true
        } else {
            isFocusable = true
        }
    }

    override fun updatePreLayout(container: ConstraintLayout) {
        super.updatePreLayout(container)

        if (this.mReferenceIds != null) {
            this.setIds(this.mReferenceIds)
        }

        mIds.forEach {
            container.getViewById(it)?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
        }
    }

    override fun onPopulateAccessibilityEvent(event: AccessibilityEvent) {
        super.onPopulateAccessibilityEvent(event)

        val constraintLayoutParent = parent as? ConstraintLayout
        if (constraintLayoutParent != null) {
            event.text.clear()

            mIds.forEach {
                constraintLayoutParent.getViewById(it)?.onPopulateAccessibilityEvent(event)
            }
        }
    }
}

Auch als Gist erhältlich: https://Gist.github.com/JulienArzul/8068d43af3523d75b72e9d1edbfb4298

Sie würden es genauso verwenden wie eine Gruppe:

<androidx.constraintlayout.widget.ConstraintLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/myTextView"
        />

    <ImageView
        Android:id="@+id/myImageView"
        />

    <com.julienarzul.Android.accessibility.ConstraintLayoutAccessibilityHelper
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:constraint_referenced_ids="myTextView,myImageView" />

</androidx.constraintlayout.widget.ConstraintLayout>

In diesem Beispiel werden TextView und ImageView aus Gründen der Barrierefreiheit in einer einzelnen Gruppe organisiert. Sie können weitere Ansichten hinzufügen, die den Fokus haben und vom Eingabehilfen-Reader im ConstraintLayout gelesen werden.

Die Ansicht ist transparent, aber Sie können den Bereich auswählen, in dem sie angezeigt werden soll, indem Sie die Attribute für das Layout mit regulären Einschränkungen verwenden.
In meinem Beispiel wird die Eingabehilfengruppe über das gesamte ConstraintLayout angezeigt. Sie können sie jedoch auch an einigen oder allen Ihrer referenzierten Ansichten ausrichten, indem Sie das Symbol app:"layout_constraint..." Attribute.

3
Julien Arzul

Legen Sie die Inhaltsbeschreibung fest

Stellen Sie sicher, dass ConstraintLayout mit einer expliziten content description fokussierbar eingestellt ist. Stellen Sie außerdem sicher, dass die untergeordneten Variablen TextViewsnot auf focusable gesetzt sind, es sei denn, Sie möchten, dass sie unabhängig voneinander ausgelesen werden.

XML

<ConstraintLayout
  Android:focusable="true"
  Android:contentDescription="artist, song, album">

    <TextView artist/>
    <TextView song/>
    <TextView album/>
    <TextView unrelated 1/>
    <TextView unrelated 2/>

</ConstraintLayout>

Java

Wenn Sie die Inhaltsbeschreibung des ConstraintLayout lieber dynamisch in Code setzen möchten, können Sie die Textwerte aus jeder relevanten TextView verketten:

String description = tvArtist.getText().toString() + ", " 
    + tvSong.getText().toString() + ", "
    + tvAlbum.getText().toString();

constraintLayout.setContentDescription(description);

Ergebnisse der Zugänglichkeit

Wenn Sie Talkback einschalten, wird das ConstraintLayout nun fokussiert und die Inhaltsbeschreibung vorgelesen.

Screenshot mit Talkback als Bildunterschrift:

 Accessibility test screen-shot

Ausführliche Erklärung

Hier ist das vollständige XML für den obigen Screenshot. Beachten Sie, dass fokussierbare Attribute und Inhaltsbeschreibungsattribute nur im übergeordneten ConstraintLayout festgelegt werden, nicht in den untergeordneten TextViews. Dies bewirkt, dass sich TalkBack nie auf die einzelnen untergeordneten Ansichten konzentriert, sondern nur auf den übergeordneten Container (dh nur die Inhaltsbeschreibung dieses übergeordneten Elements wird ausgelesen).

<?xml version="1.0" encoding="utf-8"?>
<Android.support.constraint.ConstraintLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:contentDescription="artist, song, album"
    Android:focusable="true"
    tools:context=".MainActivity">

    <TextView
        Android:id="@+id/text1"
        style="@style/TextAppearance.AppCompat.Display1"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Artist"
        app:layout_constraintBottom_toTopOf="@+id/text2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        Android:id="@+id/text2"
        style="@style/TextAppearance.AppCompat.Display1"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Song"
        app:layout_constraintBottom_toTopOf="@+id/text3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text1" />

    <TextView
        Android:id="@+id/text3"
        style="@style/TextAppearance.AppCompat.Display1"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Album"
        app:layout_constraintBottom_toTopOf="@id/text4"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text2" />

    <TextView
        Android:id="@+id/text4"
        style="@style/TextAppearance.AppCompat.Display1"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Unrelated 1"
        app:layout_constraintBottom_toTopOf="@id/text5"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text3" />

    <TextView
        Android:id="@+id/text5"
        style="@style/TextAppearance.AppCompat.Display1"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="Unrelated 2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text4" />
</Android.support.constraint.ConstraintLayout>

Verschachtelte Fokuselemente

Wenn Ihre nicht verknüpften TextViews unabhängig vom übergeordneten ConstraintLayout fokussierbar sein sollen, können Sie diese TextViews auch auf focusable=true setzen. Dies bewirkt, dass diese TextViews fokussierbar werden und einzeln ausgelesen werden, after das ConstraintLayout.

Wenn Sie die nicht verknüpften TextViews in einer einzigen TalkBack-Ankündigung gruppieren möchten (getrennt vom ConstraintLayout), sind Ihre Optionen begrenzt:

  1. Verschachteln Sie die nicht zusammenhängenden Ansichten entweder mit einer eigenen Inhaltsbeschreibung in eine andere ViewGroup oder
  2. Legen Sie focusable=true nur für das erste nicht verknüpfte Element fest und legen Sie dessen Inhaltsbeschreibung als einzelne Ankündigung für diese Untergruppe fest (z. B. "nicht verknüpfte Elemente").

Option # 2 wird als ein bisschen hackend betrachtet, würde aber die Aufrechterhaltung einer flachen Ansichtshierarchie ermöglichen (wenn Sie wirklich eine Verschachtelung vermeiden möchten).

Wenn Sie jedoch mehrere Untergruppierungen von Fokuselementen implementieren, empfiehlt es sich, die Gruppierungen als geschachtelte Ansichtsgruppen zu organisieren. In der Android-Dokumentation zur Zugänglichkeit zu natürlichen Gruppierungen :

Um das richtige Fokussierungsmuster für einen Satz verwandter Inhalte zu definieren, Platzieren Sie jedes Stück der Struktur in einer eigenen fokussierbaren ViewGroup

1
hungryghost
  1. Festlegen des Constraint-Layouts als fokussierbar (durch Festlegen von Android: focusable = "true" im Constraint-Layout)

  2. Legen Sie die Inhaltsbeschreibung auf Constraint Layout fest

  3. set focusable = "false" für Ansichten, die nicht eingeschlossen werden sollen.

Bearbeiten basierend auf Kommentaren .__: Nur anwendbar, wenn das Constraint-Layout eine einzige Fokusgruppe enthält.

0
m__