Android TextInputLayout 上的对讲错误公告

Android Talkback incorrect announcement on TextInputLayout

我的布局中有以下代码

<com.google.android.material.textfield.TextInputLayout
        android:id="@+id/tilPassword"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Password"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tilUserName"
        app:passwordToggleEnabled="true">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/tiePassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:imeOptions="actionDone"
            android:inputType="textPassword"
            android:selectAllOnFocus="true"
            android:singleLine="true" />

    </com.google.android.material.textfield.TextInputLayout>

导航到 TextInputLayout 时,TalkBack 宣布:"password password edit box"

期望的公告:"password edit box"

如果我删除 android:hint="Password"android:inputType="textPassword",它会按预期工作。

提示设置注意事项

应在 TextInputLayout 上设置提示,而不是在 TextInputEditText 或 EditText 上设置。如果在 XML 中的子 EditText 上指定了提示,TextInputLayout 可能仍能正常工作; TextInputLayout 将使用 EditText 的提示作为其浮动标签。但是,将来修改提示的调用不会更新 TextInputLayout 的提示。为避免意外行为,请在 TextInputLayout 而不是 EditText 上调用 setHint() 和 getHint()。

我认为它正在阅读您的 TextInputLayout 提示。布局不需要提示,将其移至您的 TextInputEditText。如果出于某种原因您需要那里的提示,您还可以在布局的顶部布局上使用字段 importantForAccessability="false"。

<android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password" />

        </android.support.design.widget.TextInputLayout>

通过检查源代码:

TextInputLayout class

https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/textfield/TextInputLayout.java

可以发现:

TextInputLayout 辅助功能信息通过以下方式提供 public class:

public static class AccessibilityDelegate extends AccessibilityDelegateCompat {
    private final TextInputLayout layout;

    public AccessibilityDelegate(TextInputLayout layout) {
        this.layout = layout;
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);
        EditText editText = layout.getEditText();
        CharSequence text = (editText != null) ? editText.getText() : null;
        CharSequence hintText = layout.getHint();
        CharSequence errorText = layout.getError();
        CharSequence counterDesc = layout.getCounterOverflowDescription();
        boolean showingText = !TextUtils.isEmpty(text);
        boolean hasHint = !TextUtils.isEmpty(hintText);
        boolean showingError = !TextUtils.isEmpty(errorText);
        boolean contentInvalid = showingError || !TextUtils.isEmpty(counterDesc);

        if (showingText) {
            info.setText(text);
        } else if (hasHint) {
            info.setText(hintText);
        }

        if (hasHint) {
            info.setHintText(hintText);
            info.setShowingHintText(!showingText && hasHint);
        }

        if (contentInvalid) {
            info.setError(showingError ? errorText : counterDesc);
            info.setContentInvalid(true);
        }
    }
}

并通过调用以下 public 方法将其应用于 TextInputLayout:

public void setTextInputAccessibilityDelegate(TextInputLayout.AccessibilityDelegate delegate) {
    if (editText != null) {
        ViewCompat.setAccessibilityDelegate(editText, delegate);
    }
}

所以,可以扩展:

TextInputLayout.AccessibilityDelegate class 并覆盖 onInitializeAccessibilityNodeInfo() 仅宣布需要的内容。 例如,在您的情况下,您可以执行以下操作:

private class CustomTextInputLayoutAccessibilityDelegate extends TextInputLayout.AccessibilityDelegate{

    private final TextInputLayout layout;


    public CustomTextInputLayoutAccessibilityDelegate(TextInputLayout layout) {
        super(layout);
        this.layout = layout;
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);
        EditText editText = layout.getEditText();
        CharSequence text = (editText != null) ? editText.getText() : null;
        CharSequence hintText = layout.getHint();
        CharSequence errorText = layout.getError();
        //CharSequence counterDesc = layout.getCounterOverflowDescription();
        boolean showingText = !TextUtils.isEmpty(text);
        boolean hasHint = !TextUtils.isEmpty(hintText);
        //boolean showingError = !TextUtils.isEmpty(errorText);
        //boolean contentInvalid = showingError || !TextUtils.isEmpty(counterDesc);

        if (showingText) {
            info.setText(text);
        } else if (hasHint) {
            info.setText("");
        }

        if (hasHint) {
            info.setHintText("");
            info.setShowingHintText(!showingText && hasHint);
        }

        //if (contentInvalid) {
        //    info.setError(showingError ? errorText : counterDesc);
        //    info.setContentInvalid(true);
        //}
    }
}

然后调用:

tilPassword.setTextInputAccessibilityDelegate(new CustomTextInputLayoutAccessibilityDelegate(tilPassword));

对我有用。在 EditText 提示中使用提示文本。

<android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/_10"
        android:layout_marginLeft="@dimen/_10"
        android:layout_marginRight="@dimen/_10"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="Address Line 1"/>
     </android.support.design.widget.TextInputLayout>

我们可以尝试为 AccessibilityNode 提供自定义角色描述。我们可以使用 info.isPassword 属性 检查是否显示了屏蔽密码。然后相应地构建 roleDescription。

class MyAccessibilityDelegate(val layout: TextInputLayout) : TextInputLayout.AccessibilityDelegate(layout) {

    override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
        super.onInitializeAccessibilityNodeInfo(host, info)
        val editText = layout.editText
        val content = editText?.text
        info.text = content
        info.roleDescription = if(!info.isPassword) "password edit text" else "edit text"
        info.hintText = ""
        info.isShowingHintText = false
        editText?.setSelection(content?.length ?: 0)
    }
}