【问题标题】:Android - Set TalkBack accessibility focus to a specific viewAndroid - 将 TalkBack 辅助功能焦点设置到特定视图
【发布时间】:2015-02-12 08:48:42
【问题描述】:

启用 TalkBack 后,是否可以将辅助功能焦点手册设置为特定视图?例如,当我的 Activity 启动时,我希望 TalkBack 自动关注某个 Button(视图周围的黄框)并阅读其内容描述。

到目前为止我已经尝试过:

    myButton.setFocusable(true);
    myButton.setFocusableInTouchMode(true);
    myButton.requestFocus();

requestFocus() 似乎只是请求输入焦点,与可访问性焦点无关。我也试过:

    myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
    myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
    myButton.announceForAccessibility("accessibility test");
    myButton.performAccessibilityAction(64, null); // Equivalent to ACTION_ACCESSIBILITY_FOCUS

    AccessibilityManager manager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
    if (manager.isEnabled()) {
        AccessibilityEvent e = AccessibilityEvent.obtain();
        e.setSource(myButton);
        e.setEventType(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
        e.setClassName(getClass().getName());
        e.setPackageName(getPackageName());
        e.getText().add("another accessibility test");
        manager.sendAccessibilityEvent(e);
    }

这些似乎都不起作用。

【问题讨论】:

  • “对讲开始时”。你的意思是,当我认为对讲处于活动状态时。或者,当您的视图处于活动状态时有人打开对讲?
  • 两者都不是。如果在“设置”中启用了 TalkBack,并且启动了我的应用程序的 Activity,我希望可访问性集中在我的 Activity 中的某个按钮上
  • 啊,好吧,所以当你的活动加载时,你想控制哪个项目最初由对讲焦点(android“焦点”意味着与对讲焦点不同的东西)。仅用于可访问性目的。
  • 我很好奇你为什么认为你需要这样做?通常认为将焦点自动转移到用户身上是不好的做法,事实上,除非得到适当的警告,否则根据 WCag 2.0 的准则 3.2,它本身可以被视为可访问性违规。不过,无论如何,我正在寻找答案。
  • 我的应用的目标受众是盲人。应用程序的主要操作(拍照)由特定按钮触发。我希望用户在应用启动时立即关注此按钮,而不是在到达它之前再滑动 6 次(这可能被认为很烦人)。

标签: android accessibility talkback


【解决方案1】:

免责声明:强制将 Activity 加载的焦点放在任何位置,但始终位于顶部栏(好吧,几乎永远不应该说),但实际上,只是不要这样做。这违反了 WCAG 2.0 的各种规定,包括 3.2.1 和 3.2.3,分别关于可预测的导航和上下文变化。通过这样做,您实际上可能会使您的应用更难以访问。

http://www.w3.org/TR/WCAG20/#consistent-behavior

结束免责声明。

您正在使用正确的函数调用。您需要做的就是:

myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);

问题更可能是您尝试执行此操作的点。对讲会在活动周期稍晚一点的时候附加到您的活动中。以下解决方案说明了这个问题,老实说,我不确定是否有更好的方法来做到这一点。我尝试了 onPostResume,这是 Android 操作系统调用的最后一个回调,关于加载活动,但我仍然不得不添加延迟。

@Override
protected void onPostResume() {
    super.onPostResume();

    Log.wtf(this.getClass().getSimpleName(), "onPostResume");

    Runnable task = new Runnable() {

        @Override
        public void run() {
            Button theButton = (Button)WelcomeScreen.this.findViewById(R.id.idButton);
            theButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    };

    final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

    worker.schedule(task, 5, TimeUnit.SECONDS);

}

您也许可以创建自定义视图。视图中的回调可能会提供您需要执行此操作的逻辑,而无需竞争条件!如果有时间,我可能会再研究一下。

【讨论】:

  • 谢谢。在我的应用程序中添加首选项询问用户是否将注意力集中在此按钮上会更好吗?因此,除非用户明确设置此首选项,否则将使用默认 TalkBack 行为。
  • 虽然这种方法可能适用于当前版本的 TalkBack,但我们给出的建议是切勿尝试从应用手动放置辅助功能焦点。您最终会与 TalkBack 或用户发生争执,两者都不会提供良好的体验。
  • 因此,免责声明 ;)。我想知道,为什么该事件不在受保护的空间中,仅适用于 AccessibilityServices。你应该“永远”做的事情,我同意这是每个 WCag(和常识 TalkBack 可用性)指南中的一个,似乎很适合这个类别......只是好奇,虽然我得到了 Google 代表的注意!
  • 您应该打开一个包含一些代码的新问题。如果没有代码,很难猜测会出现什么问题。
  • @ChrisCM 你应该写一篇关于 Android 辅助功能的博文。我很想了解最佳实践。比如,我们应该自动宣布工具栏标题还是让系统处理它,或者你上面提到的关于焦点的内容。就我而言,我有一个 EditText 并且它占据了焦点。我更喜欢 Toolbar Title 来使用它
【解决方案2】:

最近我遇到了同样的问题。我创建了一个 Android 扩展函数来聚焦未使用 postDelayed 聚焦的视图,就像提出的其他解决方案一样;

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)

但我遇到了另一种情况,它不起作用。但是,我得到了它:

fun View.accessibilityFocus(): View {
    this.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
    this.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED)
    return this
}

【讨论】:

  • 对于其他有同样问题的人......我发现在 Android 10 上执行常规 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) 无法与三星语音助手一起使用(奇怪的是 9 及以下很好...... ) 但是,其他解决方案是唯一可以在该设备上使用的解决方案。感谢 Gilberto,非常感谢!
【解决方案3】:

我遇到了同样的问题,因为为了保持一致的导航,我们希望选择新打开的页面的标题。 问题是屏幕阅读器选择的是页面左上角的第一个标题按钮,而不是标题。

我有一个用于整个视图的myRootView 变量和一个用于标题文本视图的myTitleView 变量。

ChrisCM 提出的将可访问性集中在正确视图上的解决方案对我很有帮助:

myTitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);

但是我仍然遇到在应用程序启动时调用此代码没有效果的问题,因为屏幕阅读器尚未准备好,并且提出等待 5 整秒的“等待对讲可用”的解决方案不是我的想要这样做,因为到那时,用户可能已经在使用界面,并且他们的选择会被自动对焦打断。

我注意到在应用打开时,左上角的标题按钮被可访问性系统地选择了,所以我编写了一个类来监听该选择并在之后立即触发我自己的选择。

这是相关类的代码:

import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public abstract class OnFirstAccessibilityFocusRunner extends AccessibilityDelegate implements Runnable {
    @NonNull
    private final View rootView;
    private boolean hasAlreadyRun = false;

    public OnFirstAccessibilityFocusRunner(@NonNull final View _rootView) {
        rootView = _rootView;
        init();
    }

    private void init() {
        rootView.setAccessibilityDelegate(this);
    }

    @Override
    public boolean onRequestSendAccessibilityEvent(@Nullable final ViewGroup host, @Nullable final View child,
        @Nullable final AccessibilityEvent event) {
        if (!hasAlreadyRun
            && event != null
            && event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
        ) {
            hasAlreadyRun = true;
            rootView.setAccessibilityDelegate(null);
            run();
        }
        return super.onRequestSendAccessibilityEvent(host, child, event);
    }
}

然后我使用这样的代码(例如在Activity的onPostResume方法中):

@Override
protected void onPostResume() {
    new OnFirstAccessibilityFocusRunner(myRootView) {
        @Override
        public void run() {
            myTitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    };
}

我花了几个小时才弄清楚如何立即将焦点转移到正确的视图上,我希望这对其他人也有帮助!

【讨论】:

    【解决方案4】:

    免责声明:似乎某些视图拦截了可访问性焦点(如 RecyclerView、PreferenceFragmentCompat),我的解决方案是“等待对讲可用”。

    Kotlin KTX 提供了在视图准备好向用户显示时执行 smth 的方法,而 Coroutine 可以帮助延迟。这不是最好的方法,但又快又简单。

    CoroutineScope(Dispatchers.Default).launch {
          delay(300)
          withContext(Dispatchers.Main) { customView.doOnLayout {it.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) }}
    }
    

    【讨论】:

    • 请不要以这种方式使用协程。有View#postDelayed,效果很好。
    【解决方案5】:

    lifecycleScope 在活动中可用。无需将延迟与任何覆盖的方法绑定。

    private fun View.focusAccessibility() = lifecycleScope.launchWhenResumed {
        delay(300)
        this@focusAccessibility.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多