Android - 将 TalkBack 辅助功能焦点设置到特定视图
Android - Set TalkBack accessibility focus to a specific view
启用 TalkBack 后,有什么方法可以将辅助功能焦点手册设置为特定视图?例如,当我的 Activity 启动时,我希望 TalkBack 自动关注某个按钮(视图周围的黄色框)并阅读其内容描述。
到目前为止我尝试过的:
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);
}
None 这似乎有效。
免责声明:强制将焦点放在 Activity 加载到顶部栏以外的任何地方总是(好吧,几乎永远不应该说总是),但实际上,不要这样做。这违反了各种 WCAG 2.0 规定,包括 3.2.1 和 3.2.3,分别涉及可预测的导航和上下文更改。这样做实际上很可能使您的应用程序更难以访问。
http://www.w3.org/TR/WCAG20/#consistent-behavior
结束免责声明。
您正在使用正确的函数调用。您只需要这样做:
myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
问题更可能是您尝试执行此操作的时间点。在 activity 周期稍晚的时候,对讲会附加到您的 activity。以下解决方案说明了这个问题,老实说,我不确定是否有更好的方法来做到这一点。我尝试了 onPostResume,这是 Android OS 调用的最后一个回调,关于活动的加载,但我仍然不得不添加延迟。
@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);
}
您或许可以创建自定义视图。视图中的回调可能会提供执行此操作所需的逻辑,而无需竞争条件!如果我有时间,我可能会在以后仔细研究它。
我遇到了同样的问题,因为为了保持一致的导航,我们希望选择新打开的页面的标题。
问题是屏幕 reader 选择了我页面左上角的第一个 header 按钮,而不是标题。
我有一个用于整个视图的 myRootView
变量和一个用于标题文本视图的 myTitleView
变量。
ChrisCM 提出的使可访问性集中在正确视图上的解决方案绝对帮助了我:
myTitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
但是我还是遇到了问题,在应用启动时调用这段代码没有效果,因为屏幕reader还没有准备好,并且提出了等待5的“等待对讲可用”的解决方案整整几秒不是我想做的事情,因为到那时,用户可能已经在使用界面,他们的选择会被自动对焦打断。
我注意到在应用程序打开时,左上角的 header 按钮被辅助功能系统地选中,所以我写了一个 class 来监听该选择并在之后立即触发我自己的选择。
这里是有问题的 class 的代码:
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);
}
};
}
我花了几个小时弄清楚如何立即将焦点放在正确的视图上,希望这对其他人也有帮助!
免责声明:似乎有些视图拦截了可访问性焦点(如 RecyclerView、PreferenceFragmentCompat),我的解决方案是一种“等待反馈可用”。
Kotlin KTX 提供了在视图准备好向用户显示时执行 smth 的方法,协程可以帮助延迟。这不是最好的方法,但又快又简单。
CoroutineScope(Dispatchers.Default).launch {
delay(300)
withContext(Dispatchers.Main) { customView.doOnLayout {it.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) }}
}
最近我遇到了同样的问题。我创建了一个 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
}
lifecycleScope
在 activity 中可用。无需使用任何覆盖方法绑定延迟。
private fun View.focusAccessibility() = lifecycleScope.launchWhenResumed {
delay(300)
this@focusAccessibility.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
}
启用 TalkBack 后,有什么方法可以将辅助功能焦点手册设置为特定视图?例如,当我的 Activity 启动时,我希望 TalkBack 自动关注某个按钮(视图周围的黄色框)并阅读其内容描述。
到目前为止我尝试过的:
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);
}
None 这似乎有效。
免责声明:强制将焦点放在 Activity 加载到顶部栏以外的任何地方总是(好吧,几乎永远不应该说总是),但实际上,不要这样做。这违反了各种 WCAG 2.0 规定,包括 3.2.1 和 3.2.3,分别涉及可预测的导航和上下文更改。这样做实际上很可能使您的应用程序更难以访问。
http://www.w3.org/TR/WCAG20/#consistent-behavior
结束免责声明。
您正在使用正确的函数调用。您只需要这样做:
myButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
问题更可能是您尝试执行此操作的时间点。在 activity 周期稍晚的时候,对讲会附加到您的 activity。以下解决方案说明了这个问题,老实说,我不确定是否有更好的方法来做到这一点。我尝试了 onPostResume,这是 Android OS 调用的最后一个回调,关于活动的加载,但我仍然不得不添加延迟。
@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);
}
您或许可以创建自定义视图。视图中的回调可能会提供执行此操作所需的逻辑,而无需竞争条件!如果我有时间,我可能会在以后仔细研究它。
我遇到了同样的问题,因为为了保持一致的导航,我们希望选择新打开的页面的标题。 问题是屏幕 reader 选择了我页面左上角的第一个 header 按钮,而不是标题。
我有一个用于整个视图的 myRootView
变量和一个用于标题文本视图的 myTitleView
变量。
ChrisCM 提出的使可访问性集中在正确视图上的解决方案绝对帮助了我:
myTitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
但是我还是遇到了问题,在应用启动时调用这段代码没有效果,因为屏幕reader还没有准备好,并且提出了等待5的“等待对讲可用”的解决方案整整几秒不是我想做的事情,因为到那时,用户可能已经在使用界面,他们的选择会被自动对焦打断。
我注意到在应用程序打开时,左上角的 header 按钮被辅助功能系统地选中,所以我写了一个 class 来监听该选择并在之后立即触发我自己的选择。
这里是有问题的 class 的代码:
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);
}
};
}
我花了几个小时弄清楚如何立即将焦点放在正确的视图上,希望这对其他人也有帮助!
免责声明:似乎有些视图拦截了可访问性焦点(如 RecyclerView、PreferenceFragmentCompat),我的解决方案是一种“等待反馈可用”。
Kotlin KTX 提供了在视图准备好向用户显示时执行 smth 的方法,协程可以帮助延迟。这不是最好的方法,但又快又简单。
CoroutineScope(Dispatchers.Default).launch {
delay(300)
withContext(Dispatchers.Main) { customView.doOnLayout {it.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) }}
}
最近我遇到了同样的问题。我创建了一个 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
}
lifecycleScope
在 activity 中可用。无需使用任何覆盖方法绑定延迟。
private fun View.focusAccessibility() = lifecycleScope.launchWhenResumed {
delay(300)
this@focusAccessibility.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null)
}